진행하고 있는 프로젝트에서 사용하고 있는 backoff 내용을 정리한다.
사용중인 메시지 브로커: RabbitMQ
1. 용어 정리
✔︎ Exchange
- Producer로 부터 전달받은 메시지를 어떤 큐들에게 발송할지 결정하는 객체
- 4가지의 타입이 있으며, 라우터 기능을 한다.
-
타입 4가지
- Direct: 라우팅 키가 정확히 일치하는 큐에 메시지 전송 (unicast)
- Topic: 라우팅 키 패턴이 일치하는 큐에 메시지 전송 (multicast)
- Headers: [key:value]로 이루어진 header값을 기존으로 일치하는 queue에 메시지 전송 (multicast)
- Fanout: 해당 exchange에 등록된 모든 큐에 메시지 전송 (broadcast)
✔︎ Binding
- Exchange와 큐를 연결하는 관계
- 모든 메시지는 Exchange가 가장 먼저 수신하는데, Exchange 타입과 binding 규칙에 따라 적절한 큐로 전달된다.
✔︎ Dead Letter Queue(DLQ)
- 오류로 인해 처리할 수 없는 메시지를 임시로 저장하는 메시지 대기열
- 소스 대기열에 처리되지 않은 메시지가 넘치지 않도록 잘못된 메시지 및 실패한 메시지의 임시 저장소 역할을 수행한다.
- DLQ에 TTL을 지정하여, 일정 시간이 지난 후 다시 작업 큐로 라우팅 되도록 한다.
일반적인 메시지 큐에서 메시지가 정상적으로 소비되지 못하면 어떻게 될까?
- 계속 큐에 들어가게 되어 무한 재시도 수행, 이는 시스템 부하의 원인이 됨
- 무한 대기 상태가 되면 큐가 가득 차서 다른 메시지도 처리 불가능
문제가 있는 메시지는 기록하고 분석할 필요가 있기 때문에 RabbitMQ, Kafka 같은 메시지 브로커는 이런 메시지를 별도의 “Dead Letter Queue(DLQ)“에 저장하도록 지원
2. 구조
담당중인 서비스의 기능 중, 파트너사에 요청한 작업 상태를 polling 방식으로 체크하여 작업 완료시 사용자에게 알림을 보내는 기능이 있다.
해당 기능은 비동기로 수행되며, 이를 위해 RabbitMQ를 사용중이고, 수행 플로우는 아래와 같다.
플로우
- 파트너사로 작업을 요청 후, polling을 위해 작업 큐에 메시지를 넣는다.
- 큐에 담긴 메시지를 consume 하여 해당 message 기반으로 파트너사 쪽으로 상태를 체크한다.
- 만약 내부 서비스 오류 또는 통신 오류 등, 재시도가 필요한 오류가 발생하게 되면, retry 큐에 들어가게 된다.
- 이때, backoff 정책에 의해, 재시도를 수행한 횟수에 따라 매번 다른 dead letter queue에 들어가게 된다.
- DLQ에 지정된 TTL이 만료되면 다시 작업 큐에 들어가게 된다.
3. Backoff 구성
플로우
- 작업에 실패하게 되면 retry.x Exchange에서 해당 작업의 재시도 횟수를 확인하여 그에 맞는 DLQ로 라우팅한다.
- DLQ에 들어온 메시지는 지정된 TTL 동안 머무른다.
- TTL이 만료되면 backoff.x Exchange로 이동하게 되고, header 값에 저장되어 있는 작업 큐 이름을 확인하여 해당 작업 큐로 requeue 된다.
- 이렇게 실패 횟수에 따라 1초, 5초, 10초 … exponential 하게 재시도 시간을 증가시키며 특정 작업을 반복한다.