개발지식 먹는 하마 님의 블로그

[내일배움캠프 79일차] 트러블 슈팅 Throttle 예외 - RateLimiter 적용 본문

내일배움캠프 (CS25)

[내일배움캠프 79일차] 트러블 슈팅 Throttle 예외 - RateLimiter 적용

devhippo 2025. 6. 16. 18:59

트러블 슈팅 - 일부 메일 발송 실패

100개의 메일 발송에 대해 비동기로 테스트 했을 때,
100개 중 일부인 5개의 메일에 대해서 발송이 실패하는 현상이 발생하였다.

(이럴 때 쓰라고 있는게 메일 로그지!)
실패 원인을 파악하기 위해 저장된 메일 로그를 확인하였다.

 

아니 AccessKey랑 SecretKey도 잘 입력되어있고, 그래서 다른 95개의 메일은 잘 발송되었는데
어째서 저 5개만 Authentication failed라는 예외가 발생한단 말인가?!

Authentication failed 예외 발생 원인

원인은 사용 중인 외부 메일 서버의 스로틀(Throttle) 정책에 의해 발생한 것이었다.

💡 Throttle이란?
이벤트 핸들러가 너무 자주 실행되지 않도록 조절하는 기법이다.

서버에 과도한 요청이 몰리는 것을 방지하기 위해 초당 요청 수를 제한해놓은 것


그럼 왜 100개 중 5개만 실패했는가?

Gmail-SMTP의 명확한 스로틀 제한 수는 밝혀진바가 없지만, 초당 2~3건 정도로 예상된다.

그렇다면 나는 다중 스레드를 통해서 4개의 요청을 동시에 보냈는데
왜 그에 대해서 실패한 결과는 없고 후반부에 발송한 5건에 대해서만 예외가 발생한 것일까?

💡 Throttle 제한은 Hard한 제한이 아니기 때문이다.
초당 요청 수는 제한되어있지만 이를 넘어간다고 해서 바로 예외를 던지지는 않는다.

제한된 수를 넘어간 요청은 해당 서버에서 관리하는 Queue에 들어가게 된다.
Queue의 용량이 꽉 찬 상태에서 새로운 요청이 들어오는 경우 예외를 던지는 것이다.

 

해결 방법 - Rate Limiter

Rate Limiter는 요청의 간격을 제어하여 스로틀 제한에 걸리지 않는 속도로 요청을 보낼 수 있도록 도와준다.

Rate Limiter 기능을 제공하는 보편적인 라이브러리에는 3가지가 있다.

  • Guava
  • Resilience4J
  • Bucket4J

그 중에서 나는 Bucket4J를 선택해 적용하였다.

 

Bucket4J를 선택한 이유

처음에는 가장 간단하고 사용 글이 많은 Guava를 사용하고자 했다.
그런데 IntelliJ에서 경고가 떴다. "지원이 종료됩니다." 라고

찾아보니 유지보수가 종료된다는 이야기가 있었다.

그래서 Resilience4J와 Bucket4J의 차이점에 대해 알아보았다.
그리고 다음과 같은 이유로 Bucket4J를 선택하였다.

  • 다른 기능들도 제공해서 무거운 Resilience4J와 달리 RateLimiter 기능만 제공해서 가벼운 Bucket4J
  • 분산 환경과의 호환성 높음

 

Bucket4J의 원리

Bucket4J는 TokenBucket 알고리즘 기반이다.

양동이 하나에 토큰을 채운 후 토큰이 있으면 발급, 없으면 토큰이 생길 때까지 대기하는 것이다.

https://www.geeksforgeeks.org/computer-networks/token-bucket-algorithm/

동작 방식이 매우 쉽다.

 

프로젝트에 적용