Lewis's Tech Keep
[Ratelimiter] 처리율 제한 장치 본문
처리율 제한 장치(rate limiter)란?
클아이언트 또는 서비스가 보내는 트래픽의 처리율(rate)을 제어하기 위한 장치
특징
- 특정 기간 내에 전송되는 클라이언트의 요청 횟수를 제한
- 요청 횟수가 임계치를 넘어가면 추가로 도달한 모든 호출은 처리가 중단(block)
이점
- DoS 자원 고갈 방지
- 비용 절감
- 추가 요청을 block 하기 때문에 더 많은 서버 자원이 필요하지 않게 됨
- 서버 과부하 방지
- bot에서 오는 트래픽이나 사용자의 잘못된 이용패턴으로 유발된 트래픽을 걸러내는데 활용
요구되는 사항들
- 낮은 응답시간
- 가능한 적은 메모리
- 분산형 처리율 제한
- 여러 서버나 프로세스에서 공유
- 예외 처리 - 발생한 경우에 사용자에게 분명히 보여주기
- 높은 fault tolerance : 제한 장치에 장애가 생기더라도 전체 시스템에 영향을 주어선 안됨
처리율 제한 알고리즘
- 토큰 버킷(token bucket)
- 누출 버킷(leaky bucket)
- 고정 윈도우 카운터(fixed window log)
- 이동 윈도우 로그(sliding window log)
- 이동 윈도우 카운터(sliding window counter)
토큰 버킷
- 지정된 용량의 컨테이너를 가지고 있음
- 토큰이 꽉 찬 버킷에는 더 이상의 토큰은 추가되지 않음
- 넘치면 추가 공급된 토큰이 버려짐
누출 버킷
토큰 버킷 알고리즘과 비슷하지만 요청 처리율이 고정되어 있음(생각해봣는데 비유동적으로도 가능할듯), FIFO 큐로 구현
- 요청이 도착하면 큐가 가득차 있는지 확인, 빈자리가 있는 경우에는 큐에 요청을 추가
- 큐가 가득 차 있으면 새 요청을 버림
- 지정된 시간마다 큐에서 요청을 꺼내어 처리 (굳이 고정으로 안해도 될듯?)
고정 윈도우 카운터 알고리즘 (fixed window counter)
resilience4j ratelimiter 사용 방식
- 타임라인을 고정된 간격의 윈도(window)로 나누고 각 윈도마다 카운터(counter)를 붙인다.
- 요청이 접수될 때마다 카운터의 값은 1씩 증가함
- 카운터의 값이 사전에 설정된 임계치에 도달하면 새로운 요청은 새 윈도가 열릴 때까지 버려짐
- 경계 부근에서 일시적으로 많은 트래픽이 몰려드는 우 기대했던 시스템의 처리 한도보다 많은 양의 요청을 처리하게 됨
이동 윈도 로깅 알고리즘
Spring Cloud Gateway RateLimiter 방식
- 요청 타임스탬프를 추적한다. 타임 스탬프 데이터는 Redis 같은 캐시에 보관
- 새 요청이 오면 기존 만료된 타임스탬프 제거
- 새 요청의 타임스탬프를 로그에 추가함
- 로그의 크기가 허용치보다 같거나 작으면 통과 아니면 거부
- 다량의 메모리를 사용함
- 거부된 요청의 타임스탬프도 보관하기 때문
이동 윈도 카운터 알고리즘
- 고정 윈도 카운터 알고리즘과 이동 윈도 로깅 알고리즘을 결합
- 현재 1분간 요청 수 + 직전 1분간 요청수 * 윈도우간 겹치는 비율
- 위 그림 경우 250 + 400 * 0.25가 됨
내가 사용했었던 경험
실제로 인증 서버 & Gateway API에서 어플리케이션 간에서 처리율 제한을 위해 Ratelimiter4j Ratelimiter 라이브러리를 사용했었다.
하지만 kubernetes위에 올라가 있는 멀티 pod들을 관리할 수 있는 처리율을 Redis와 같은 공통 관리 저장소에서 관리한 것은 아니었다.
그래서 초당 300개를 받고 싶으면 각 파드 당 처리율 제한을 설정하였다.
💡 ex. rate limit 30 = 300 / 10 (pod 개수)
이런 형태로 나누어 넣게 되었는데, 이렇게 하니 실제로는 각자 다른 카운트를 세고 있어서 짧은 시간에 엄청 많은 트래픽이 들어오는 경우에 다 같이 처리율 제한 오류를 반환하는 것이 아닌 일부만 독립적으로 반환하는 경우도 있었다.
redis에 데이터를 저장하게 되는 것에 대해 네트워크 용량 및 라이브러리 미제공 이슈로 인한 커스텀 코드 관리 이슈도 같이 있었기에 결국 도입하지는 않았지만 도입하는 것이 필요하다면 바로 도입하는 것이 좋을 것 같다.
생겼던 이슈 정리
- 파드 개수는 scale-out 이 유연하게 처리하는 경우(ex. auto-scaling) 빠르게 변화할텐데 이때마다 숫자를 바꾸는 것을 신경써야했다.
- 엄청 짧은 시간에 한쪽 파드에 몰리는 경우 resilience4j ratelimiter는 세마포어로 스레드 단에서 관리되기 때문에 스레드 점유가 높아져 장애가 발생할 수 있었다.
-
'Java > Spring' 카테고리의 다른 글
[Spring Boot][JDK21] virtual threads 이슈 (0) | 2024.06.27 |
---|---|
[Spring Boot] Spring Hibernate Envers REV INFO 타입 변경 (0) | 2024.06.27 |
[Spring] h2를 이용한 테스트 시 user 테이블 테스트를 실패했을 때 확인 (1) | 2023.04.25 |
Comments