티몬이 상품 정보 갱신 파이프라인에 RabbitMQ를 도입하며 메시지 손실을 줄이려고 잡은 브로커 설정을 정리한 1부다. 가격이 바뀌었는데 이전 값이 노출되면 안 되는 성격이라 신뢰성에 무게를 뒀다. 클러스터링, 지속성, 큐 미러링 순으로 이어지고 컨슈머 쪽은 2부로 넘긴다.
핵심 포인트- 솔루션 선정 기준은 다양한 플랫폼과 언어 지원, 메시지 신뢰성 기능, UI 관리 도구였다.
- 상품 정보 갱신이 반드시 MQ를 거치므로 메시지가 유실되면 잘못된 가격이 노출된다.
- 단일 노드는 그 노드가 죽으면 전면 장애가 되므로 여러 노드를 클러스터로 묶는다.
- 클러스터 노드는 RAM 모드와 DISK 모드를 고를 수 있는데 손실 방지를 위해 DISK 모드를 권한다.
- Exchange와 Queue에 durable을 주어야 브로커 재시작 후에도 이전 구성이 복원된다.
- 같은 클러스터라도 큐의 메시지는 공유되지 않으므로 미러링 정책을 걸어야 한다.
상세 정리- 도입 배경: 메시지를 발생시키고 받아 처리하는 시스템이 필요했고, 직접 만들 수도 있지만 검증된 솔루션 중에서 고르기로 했다.
- RabbitMQ 선택 이유: 요구 조건을 충족하면서 유연한 라우팅과 클러스터링을 제공하고 오픈소스이면서 상업적 지원도 가능하다.
- 적용 흐름: 상품 관리자가 정보를 입력하면 DB에 적재된 뒤 사용자에게 보이는데, 갱신은 반드시 MQ를 통해 메시지를 받아야 반영된다.
- 신뢰성이 중요한 이유: 가격이 바뀌었는데 이전 가격이 계속 노출되면 큰 사고이므로 운영의 중심을 손실 방지에 둔다.
- 구성 주체 셋: 메시지를 보내는 프로듀서, 전달하는 브로커, 받아 소비하는 컨슈머로 나뉜다. 1부는 브로커를 다룬다.
- 클러스터링의 목적: 한 노드가 예기치 못한 오류로 멈추면 상품 정보가 전혀 갱신되지 않는 전면 장애가 되므로 여러 노드로 묶는다.
- 클러스터 구성 주의점: 모든 노드가 같은 Erlang 쿠키를 갖고 같은 버전의 RabbitMQ와 Erlang으로 돌아야 하며 네트워크는 LAN으로 구성해야 한다.
- 모드 선택: RAM 모드는 처리 시간이 줄어 성능이 오르지만 영속성을 보장하지 못하고, DISK 모드는 처리 시간이 늘지만 손실을 막는다.
- 모드 결정: 초당 1~2건 수준이고 큐에 쌓지 않고 즉시 처리하므로 성능을 위해 RAM 모드를 택할 이유가 없다고 판단했다.
- 지속성 문제: 브로커가 재시작되면 Exchange와 Queue가 이전 정보 그대로 재구성돼야 하는데, 그렇지 않으면 메시지를 보내지도 받지도 못한다.
- 손실 시나리오: 처리할 Exchange가 없는 상태로 메시지를 보내면 그대로 유실된다. 프로듀서가 Confirm 모드라면 라우팅되지 못했다는 통지는 받을 수 있다.
- 지속성 설정: exchangeDeclare와 queueDeclare에 durable을 true로 넘겨 재가동 시 복원되게 한다.
- 미러링의 필요성: 같은 클러스터의 노드라도 큐의 메시지는 서로 다르므로, 한 노드가 멈추면 그 큐의 메시지는 노드가 살아나기 전까지 복구되지 않는다.
- 미러링 동작: 새 노드가 클러스터에 참여하면 메시지를 제외한 구성은 동일해지지만 큐는 비어 있다. 미러링을 걸어야 다른 노드로도 메시지를 보낼 수 있다.
- 설정 방법: rabbitmqctl의 set_policy 명령으로 호스트와 정책명, 큐 이름 패턴, 파라미터를 지정해 미러링 정책을 건다.
왜 읽나메시지 유실이 곧 잘못된 데이터 노출로 이어지는 파이프라인이라면 브로커 쪽에서 먼저 잠가야 할 설정 세 가지가 정리돼 있다.