FrameCheckMate에서 담당한 부분은 이메일 전송 서버 구축이다.
이번 회고는 이메일 전송 서버를 구축하기까지의 사고 과정을 담고 있다.
Email 전송 과정
Email 전송을 수행하는 상황은 다음과 같다.
Card Server와 Member Server에서 이벤트가 발생한 경우 이메일 전송 Request를 Notification Server로 전송하고 Notification Server에서는 사용자에게 Email을 전송한다.
Email 전송 발생 Event
- Card Server(Card는 각 팀원의 작업 단위이다.)에서 이메일을 전송하는 경우
- 사용자에게 작업이 할당된 경우
- 작업을 완료하고 Confirm 요청한 경우
- Confirm이 관리자에 의해 승인된 경우
- Confirm이 관리자에 의해 반려된 경우
- Member Server에서 이메일을 전송하는 경우
- 회원 가입이 완료된 경우
- (일반 회원) Project에 초대된 경우
- (관리자) Project를 생성한 경우
첫 번째 고찰점 : 서버 간 전송
- 서버 간 전송이 필요한 상황에서 처음엔 WebClient(RestClient) 등의 방법을 생각했다.
하지만 다음과 같은 의문이 생겼다.
Notification Server로 요청이 집중된다면 ?, 또 Notification Server에서 요청 처리에 실패한다면 ?
- 그렇다. 위 구조의 경우 서버 간 결합도가 크고 장애 발생에 대한 대처를 하기 어렵다.
- 서버 간 전송에서 결합도를 낮추고 메시지 유실에 대처하기 위해 RabbitMQ를 도입했다.
RabbitMQ 적용
- 변경된 전송 로직
- Card, Member 서버에서는 Email 전송 요청을 RabbitMQ Queue로 보낸다.
- Notification Server는 Consumer로서 큐에서 메시지를 꺼내 Email을 전송한다.
- 2번 과정에 있어 이메일 전송이 실패한 경우 최대 2번 재전송한다.
Notification RabbitMQ Listener
Notification RabbitMQ Retry
RabbitListener에 대한 재시도 전략을 적용해줬다.
두 번째 고찰점 : 재시도 실패 시
RabbitMQ의 Listener 재시도를 도입해 Consumer(Notification Server)가 실패 시 다시 데이터를 처리하도록 적용했다.
하지만 위의 방식은 완벽하지 않고 문제가 있다는 것을 알았다.
만약 재시도 시에도 실패한다면 어떻게 해야할까?
- 단순히 재시도 전략을 구축하더라도 실패하여 처리되지 못하는 경우가 발생한다고 생각한다.
- 메시지 재시도가 실패하는 경우는 무엇일까?
재시도가 실패하는 경우
-
메시지의 형식이 불완전하거나 전송되면 안되는 경우
- 로직 상 Member Server와 Card Server는 DB로부터 조회된 사용자의 Email을 바탕으로 Notification Server에 요청을 보낸다.
- 또한 메일 내용은 Notification Server 자체에서 관리되는 “문자열”을 전송한다.
결론적으로 메시지 형식에 문제가 생기는 경우는 없을거라 판단
-
메일 시스템에 장애가 발생하는 경우
- 이메일 시스템에 장애가 발생한 경우 메일 요청은 계속 재시도하더라도 실패하게 될 것이다.
- 나는 Consumer(Notification Server)와 RabbitMQ 사이의 장애 대응만 신경 쓰면 된다 생각했지만 결국 메일 전송 자체에 문제가 생기면 올바르게 작동하지 않을 것이다.
메일 시스템에 장애가 발생하는 경우의 대응을 구축해야 한다 !
DLQ(Dead Letter Queue)
참고 : https://aws.amazon.com/ko/what-is/dead-letter-queue/
- DLQ(Dead Letter Queue)는 소프트웨어 시스템에서 오류로 인해 처리할 수 없는 메시지를 임시로 저장하는 특수한 유형의 메시지 대기열
- Dead Letter를 적용하고 Dead Letter Queue에 쌓이는 메시지는 Slack을 통해 확인하도록 구축했다.
Dead Letter Queue 적용
Dead Letter Queue 생성 코드
Member Queue에 DLQ 적용
Message Queue & DLQ Logic