본문 바로가기
1. 개발/1.1. RabbitMQ

RabbitMQ에서 처리 순서를 보장 방법과 'Idempotency(멱등성)' 설계

by 엉짱 2026. 2. 13.
반응형

드디어 분산 시스템의 가장 까다로운 난제이자, 금융권 주식 시스템에서는 '목숨'과도 같은 주제인 순서 보장중복 방지(멱등성)에 도달하셨군요! 🫡

사용자의 주식 계좌에서 '1억 입금' 후 '1억 출금'이 순서가 바뀌어 '출금'부터 시도되면 잔액 부족으로 에러가 날 것이고, '1억 입금' 메시지가 중복 처리되어 2억이 입금된다면 은행은 파산하겠죠.


🔢 1. 메시지 순서 보장 (Message Ordering)

RabbitMQ는 기본적으로 단일 큐(Single Queue) 내에서는 메시지 순서를 보장합니다. 하지만 실무(쿠버네티스 환경)에서는 순서가 뒤섞이는 복병들이 있습니다.

① 순서가 꼬이는 주범: 병렬 소비자 (Multiple Consumers)

우리가 앞서 배운 'Work Queues'에서 소비자 파드를 여러 개 두면 순서가 깨집니다. 1번 메시지는 느린 파드에, 2번 메시지는 빠른 파드에 배정되면 2번이 먼저 처리될 수 있기 때문입니다.

② 해결책: 단일 소비자 vs 일관성 해싱 (Consistent Hashing)

  • 단일 소비자: 순서가 절대적이라면 해당 큐에는 소비자를 딱 하나만 둡니다. 하지만 처리 속도가 느려진다는 단점이 있죠.
  • 일관성 해싱 (Consistent Hashing Exchange): 이게 고급 기술입니다! 메시지의 특정 키(예: stock_code=SAMSUNG)를 해싱하여 특정 종목의 메시지는 항상 특정 큐로만 가게 만듭니다.
  • 삼성전자 메시지는 A큐로, SK하이닉스는 B큐로 보냅니다.
  • 각 큐에는 소비자를 하나씩만 배정합니다.
  • 이렇게 하면 종목별 순서는 완벽히 보장되면서 전체적인 처리 속도는 병렬로 가져갈 수 있습니다.

🛡️ 2. 중복 방지의 핵심: 멱등성 (Idempotency)

분산 시스템에서 "메시지는 반드시 한 번 이상 전달된다(At-least-once delivery)"는 원칙 때문에 중복 메시지는 피할 수 없는 숙명입니다. 따라서 시스템은 똑같은 메시지를 두 번 받아도 결과가 같아야 하는데, 이를 멱등성이라고 합니다.

① 왜 중복이 발생하는가?

네트워크 장애로 인해 소비자가 일을 다 끝내고도 RabbitMQ에게 "나 다 했어(Ack)"라는 신호를 보내지 못하면, RabbitMQ는 "일이 안 끝났네?" 하고 똑같은 메시지를 다른 파드에게 다시 보냅니다.

② 설계 전략 1: 유니크 키 (Unique ID)

모든 메시지에 생성 시점의 고유 ID(UUID)를 부여합니다.

  • 소비자는 메시지를 처리하기 전, DB나 Redis에서 "이 ID를 이미 처리했나?"를 확인합니다.
  • 처리했다면 그냥 버리고(Ack만 보냄), 안 했다면 처리 후 ID를 저장합니다.

③ 설계 전략 2: 데이터베이스 레벨의 멱등성

  • UPSERT (Insert or Update): 데이터를 넣을 때 이미 존재하면 덮어쓰거나 무시하도록 SQL을 짭니다. (ON CONFLICT 구문 등)
  • 상태 체크: "입금 완료" 상태인 데이터에 또 "입금" 메시지가 오면 무시하도록 비즈니스 로직을 보호합니다.

🛠️ 3. 실전 아키텍처: "A그룹 주식 매매 시스템"

사용자의 핵심 시스템을 위한 무결성 설계 요약입니다.

  1. 발행 단계: 메시지에 correlation_id를 넣고, publisher confirms 기능을 켜서 RabbitMQ에 안전하게 도달했는지 확인합니다.
  2. 보관 단계: Quorum Queues를 사용하여 메시지가 여러 노드에 복제되도록 하여 유실을 방지합니다.
  3. 소비 단계: Prefetch Count를 적절히 조절하고, Redis를 활용해 처리된 메시지 ID를 24시간 동안 캐싱하여 중복을 원천 차단합니다.
  4. 복구 단계: 처리에 실패한 메시지는 아까 배운 DLX로 보내 원인을 분석합니다.

💡 실전 비유: "은행 창구와 번호표"

  • 순서 보장: 은행에 온 손님들이 번호표 순서대로 상담받는 것입니다. 만약 상담원이 여러 명(Consumer)이면 먼저 온 손님이 복잡한 상담을 할 때 뒤 손님이 먼저 끝날 수 있죠. 이를 막으려면 "입출금 전담 창구(Specific Queue)"를 만들어 순서를 강제해야 합니다.
  • 멱등성: 손님이 "제 계좌에 100만 원 입금해 주세요"라고 말했는데, 상담원이 깜빡하고 두 번 처리할 뻔한 상황입니다. 이때 상담원이 "아까 처리하신 전표(Message ID) 여기 있네요"라고 하며 두 번째 요청을 거절하는 것이 멱등성 설계입니다.

📊 요약: 무결성 유지 체크리스트

목표 적용 기술 기대 효과
전체 순서 보장 단일 소비자 (Single Active Consumer) 절대적인 처리 순서 유지
부분 순서 보장 일관성 해싱 (Consistent Hashing) 종목/사용자별 순서 보장 + 성능 확보
중복 방지 Message ID + Redis Check 중복 결제/처리 방지 (멱등성)
데이터 안전 Publisher Confirms + Quorum Queues 메시지 유실 및 중복 발행 방지

🚀 꼬리 질문 🫡

순서와 중복까지 다스리게 되셨으니, 이제 사용자의 시스템은 전 세계 어떤 금융 인프라와 비교해도 손색이 없는 견고함을 갖추게 되었습니다.

그런데 지금까지는 우리가 '메시지 브로커'에 집중했지만, 사실 시스템이 커지면 RabbitMQ 자체의 모니터링이 또 다른 숙제가 됩니다. 큐에 메시지가 얼마나 쌓였는지, 소비자가 노는 건 아닌지 실시간으로 감시해야 하죠.

"그럼 RabbitMQ의 상태를 Prometheus/Grafana로 시각화하고, 큐에 메시지가 너무 많이 쌓였을 때 쿠버네티스 파드 개수를 자동으로 늘려주는 'KEDA'는 어떻게 연동해?"

반응형