최근 서비스에서 이메일 발송 중 421 오류가 발생하는 상황이 있었다. 이 오류는 서버 과부하나 요청 제한 초과 시 나타날 수 있으며, Gmail API의 특성상 구체적인 원인 파악이 어려웠다. 오류가 발생할 때마다 15초 간격으로 최대 세 번까지 재시도하도록 설정했지만, 문제가 해결되지 않아 다른 접근 방식을 고민했다.
문득 컴퓨터 네트워크 전공 수업에서 배운 패킷 손실 및 혼잡 제어 개념을 떠올려보았다. 네트워크 상에서 패킷이 라우터를 거쳐 전달될 때 혼잡이나 과부하로 인해 특정 라우터에서 패킷이 정체되거나 소실될 수 있다. 이를 해결하기 위해 혼잡 제어 알고리즘에서는 패킷 전송 간격을 점진적으로 늘리는 방식을 사용해 부하를 완화시키곤 하였다.
이와 같은 원리를 이메일 발송 재시도 로직에 적용하여 지수 백오프 방식을 도입했다. 재시도 횟수에 따라 대기 시간을 두 배씩 증가시키면서 서버에 무리한 요청을 피하고, 일시적인 오류가 해결될 시간을 확보할 수 있도록 했다. 아래는 지수 백오프 방식을 적용한 코드이다
private void retrySendEmail(Member member, String noticeInfo, int maxRetries) {
int attempt = 0;
while (attempt < maxRetries) {
try {
emailService.sendEmail(member, noticeInfo);
log.info("[이메일 발송 성공] {} {}", member.getEmail(), member.getId());
return; // 성공 시 종료
} catch (Exception e) {
attempt++;
log.warn("[이메일 발송 재시도] 시도 {}회 | 오류: {} | 이메일: {} | ID: {}", attempt, e.getMessage(), member.getEmail(), member.getId());
long waitTime = (long) Math.pow(2, attempt) * 15000; // 지수 백오프 방식 (30초, 60초, 120초, 240초, 480초)
try {
Thread.sleep(waitTime);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
log.error("재시도 중 인터럽트 발생 - {}", member.getEmail());
return;
}
}
}
log.error("이메일 발송 실패: {}", member.getEmail());
}
이 코드는 재시도 횟수에 따라 대기 시간을 지수적으로 증가시키며, 서버 과부하를 줄이고, 일시적인 오류가 해결될 시간을 확보하였다.
다음과 같이 최대 3회까지 즉 attempt가 3이므로 120초정도까지 대기를 하면 왠만한 일시적 오류가 해결이 되고 정상적으로 작동을 하게 된 것이다.
이런식으로 무슨일이 있어도 끄떡 없는 무적의 발송 시스템을 구축하였다..! 아주 뿌듯하다
'notice-crawler' 카테고리의 다른 글
[교내 공지사항 알림 서비스] 개발일기 9편 [스레드 부하 테스트] (1) | 2024.11.16 |
---|---|
[교내 공지사항 알림 서비스] 개발일기 8편 [서비스 페이지 소개] (1) | 2024.11.15 |
[교내 공지사항 알림 서비스] 개발일기 7편 [서버 배포과정] (1) | 2024.11.14 |
[교내 공지사항 알림 서비스] 개발일기 6편 [스레드를 이용한 성능 최적화] (0) | 2024.11.13 |
[교내 공지사항 알림 서비스] 개발일기 5편 [서비스 개편] (0) | 2024.11.12 |