메세지 큐
생상자와 소비자 사이에서 메세지 ( 데이터 )를 임시로 저장하고 전달하는 중간 저장소
메세지 큐를 사용하는 이유
비동기 처리와 작업 저장
여러 작업을 비동기적으로 처리하고, 메세지 큐에 저장함으로써 작업의 유실을 방지합니다.
작업을 처리하는 과정에서 문제 발생 시 Dead Letter Queue에 실패한 메시지를 모아 나중에 분석하거나 재처리할 수 있습니다.
장애 격리
시스템 간의 결합도를 낮춰 장애 전파를 막습니다.
확장성
생산자와 소비자가 분리됨으로써, 소비자를 수평 확장할 수 있으며, 트래픽 증가 시에도 소비자 인스턴스를 추가히여 병렬 처리가 가능합니다. 이를 통해 시스템 전체의 처리량을 높힐 수 있습니다.
트래픽 완충
순간적인 트래픽 폭주 상황에서 메세지 큐가 버퍼 역할을 수행하여 요청을 저장하고, 소비자가 처리 가능한 속도로 작업 처리 속도를 조절하여 시스템 과부화와 장애를 방지하며 안정성을 높힙니다.
[ 사용자가 회원가입을 완료하면, 환영 이메일 발송, 가입 포인트 지급, 카카오톡 알림 발송과 같은 후속 작업이 함께 수행된다면 ]
@Transaction
public void signUp(SignUpRequest request) {
Member member = memberRepository.save(new Member(request)); // 회원 가입
emailService.sendEmail(member.getEmail()); // 이메일 발송
pointService.payPoint(member.getId()); // 포인트 지급
notificationService.sendNotification(member.getId()); // 알림 발송
}
이 일련의 작업이 동기적으로 진행된다면 불필요하게 오랜 시간이 소요되게 되고, 만약 작업들을 진행하던 중 장애가 발생한다면 회원가입을 실패하게 됩니다.
만약 작업을 메세지 큐를 사용하여 비동기적으로 처리한다면
@Transaction
public void signUp(SignUpRequest request) {
Member member = memberRepository.save(new Member(request)); // 회원 가입
messageQueue.publish(new memberSignUpEvent(member.getId()));
}
// 이벤트 처리 함수
...
회원가입을 제외한 나머지 작업을 비동기적으로 처리하여 작업 시간을 크게 단축할 수 있으며, 작업 중 하나가 실패하더라도 회원가입은 성공하게 됩니다.
( 실패한 작업은 이후 재시도하게 됩니다. )
특징
비동기 통신
생산자는 메세지를 큐에 넣고 바로 다음 작업을 수행하며, 소비자가 언제 처리하든 신경쓰지 않습니다.
내구성
메세지 ( 데이터 )를 디스크에 저장하여 시스템 장애 시에도 유실을 방지합니다.
버퍼링
트래픽이 급증해도 큐가 메세지를 쌓아두고 소비자가 자신의 설정 값에 맞춰 소비합니다.
메세지 큐의 종류
Apache Kafka
RabbitMQ
AWS SQS
[ Apache Kafka와 RabbitMQ의 차이 ]
Kafka는 분산 로그 기반의 메시징 플랫폼으로, 대용량 데이터 스트리밍과 이벤트 처리에 적합하며 메시지를 디스크에 일정 기간 저장하여 재처리할 수 있는 구조를 제공합니다. 높은 처리량과 수평 확장성이 강점으로, 로그 수집이나 사용자 행동 데이터와 같은 이벤트 기반 시스템에 주로 사용됩니다.
분산 로그 기반 : 파티션 단위의 로그를 여러 브로커에 분산 배치하고 복제하여 고가용성과 높은 처리량을 확보하는 방식
RabbitMQ는 AMQP 기반의 메시지 브로커로, 메시지를 큐 단위로 전달하고 소비자의 확인 응답( ACK )을 통해 메시지 처리를 보장합니다. 다양하고 복잡한 라우팅을 지원하며, 메시지의 정확한 전달과 작업 단위 처리가 중요한 경우에 적합합니다.
AMQP 기반 : 메시지의 전송/라우팅/확인/재전송 방식을 표준 프로토콜로 정의한 것으로, RabbitMQ는 이를 기반으로 신뢰성과 라우팅 기능이 뛰어난 메시징 시스템
이처럼 Kafka는 대규모 트래픽과 스트리밍 처리에 강점을 가지는 반면, RabbitMQ는 정교한 메시지 제어와 신뢰성이 필요한 작업 처리에 강점을 가집니다.
RabbitMQ는 메시지가 소비자에 의해 정상적으로 처리되었는지를 추적하여 성공 시 해당 메시지를 큐에서 삭제하지만, Kafka는 메시지를 즉시 삭제하지 않고 로그에 지속적으로 저장하며, 소비자가 어디까지 읽었는지만을 기록합니다.
[ 메세지 유실 시 어떻게 할까 ]
생산자 쪽에서 유실 시 메세지 발행의 성공 여부를 파악하여 성공 응답을 받지 못한 경우 재시도를 수행함으로써 메세지 큐에 전달되기 전의 유실을 방지하고, Broker 단계에서는 메시지를 메모리에만 보관하지 않고 디스크에 영속화하고, 복제 구성을 통해 브로커 장애가 발생하더라도 메시지가 유지되도록 합니다.
Consumer 단계에서는 메시지를 실제로 처리한 이후에만 처리 완료 상태를 기록해야 합니다. 처리 도중 오류가 발생한 경우에는 메시지를 재처리하거나 별도의 DLQ로 보내 추후 복구가 가능하도록 합니다.
[ 같은 메세지가 중복 처리되면 어떻게 할까 ]
각 메세지에 고유한 ID를 부여하고 이를 통해 멱등성을 적용하거나 데이터베이스의 unique constraint를 활용하여 중복 처리를 차단합니다.
[ 메세지 큐를 사용했을 때의 단점 ]
당연히 시스템 복잡도가 증가하고 메세지 큐 자체가 SPOF가 될 수 있어 고가용성 구성이 필요하며 데이터 정합성 관리가 복잡해집니다.