메시지 브로커 아키텍처
도시의 우편 시스템을 생각해보자. 발신자가 편지를 우체통에 넣으면, 우체부가 이를 수거하여 우체국으로 운반한다. 우체국에서는 편지를 목적지별로 분류하고, 해당 지역의 배달부가 최종 수신자에게 전달한다. 이 과정에서 우체국은 발신자와 수신자를 직접 연결하지 않으면서도 안정적인 메시지 전달을 보장하는 중개자 역할을 한다. 컴퓨터 시스템의 메시지 브로커도 이와 유사한 역할을 수행한다. 메시지를 생성하는 프로듀서와 이를 소비하는 컨슈머 사이에서 메시지를 중계하며, 양측이 서로의 존재나 상태를 알 필요 없이 통신할 수 있게 한다.
메시지 브로커의 핵심 아키텍처는 두 가지 기본 모델로 구분된다. 첫 번째는 점대점(Point-to-Point) 모델로, 하나의 메시지가 정확히 하나의 컨슈머에게만 전달된다. 이는 작업 분배에 적합한 모델로, 여러 워커 중 하나가 각 작업을 처리하도록 보장한다. 은행의 거래 처리 시스템을 예로 들면, 각 거래 요청은 한 번만 처리되어야 하므로 점대점 모델이 적절하다. 두 번째는 발행-구독(Publish-Subscribe) 모델로, 하나의 메시지가 해당 주제를 구독한 모든 컨슈머에게 전달된다. 뉴스 배포 시스템처럼 동일한 정보를 여러 수신자에게 동시에 전파해야 할 때 유용하다.
메시지 라우팅은 브로커의 핵심 기능 중 하나다. 단순히 모든 메시지를 모든 컨슈머에게 전달하는 것은 비효율적이므로, 메시지를 적절한 목적지로 안내하는 정교한 메커니즘이 필요하다. 토픽 기반 라우팅에서는 메시지에 주제를 태그하고, 컨슈머는 관심 있는 주제만 구독한다. 예를 들어, 전자상거래 시스템에서 "주문.생성", "주문.취소", "결제.완료" 등의 토픽을 정의하고, 재고 관리 서비스는 주문 관련 토픽만, 회계 서비스는 결제 관련 토픽만 구독할 수 있다. 더 복잡한 라우팅이 필요한 경우 콘텐츠 기반 라우팅을 사용한다. 메시지의 실제 내용을 검사하여 라우팅 결정을 내리는 방식으로, SQL과 유사한 필터 표현식을 사용하여 "금액이 100만원 이상인 주문" 같은 조건을 지정할 수 있다.
메시지의 지속성은 시스템의 신뢰성을 좌우하는 중요한 요소다. 메모리에만 메시지를 저장하면 처리 속도는 빠르지만, 브로커가 재시작되면 모든 메시지가 손실된다. 반면 모든 메시지를 디스크에 저장하면 안전하지만 성능이 저하된다. 현대의 메시지 브로커는 이 두 가지 접근법을 적절히 조합한다. 중요한 메시지는 디스크에 즉시 기록하고, 상대적으로 덜 중요한 메시지는 메모리에 버퍼링했다가 주기적으로 디스크에 기록한다. 또한 저널링 기법을 사용하여 메시지를 순차적으로 기록함으로써 디스크 I/O 성능을 최적화한다.
메시지 전달 보장은 분산 시스템의 복잡성을 잘 보여주는 영역이다. 가장 단순한 것은 "최대 한 번(At-most-once)" 전달로, 메시지가 전달되지 않을 수는 있지만 중복 전달은 없다. 로그 수집처럼 일부 데이터 손실이 허용되는 경우에 적합하다. "최소 한 번(At-least-once)" 전달은 메시지가 반드시 전달되지만 중복될 수 있다. 대부분의 비즈니스 애플리케이션에서 선호되는 방식으로, 컨슈머가 멱등성을 보장한다면 안전하게 사용할 수 있다. "정확히 한 번(Exactly-once)" 전달은 이상적이지만 구현이 매우 어렵다. 트랜잭션 ID와 상태 추적을 통해 근사적으로 구현할 수 있으나, 성능 오버헤드가 크다.
브로커 클러스터링은 확장성과 고가용성을 달성하는 핵심 기술이다. 단일 브로커로는 처리할 수 있는 메시지 양에 한계가 있고, 장애 시 전체 시스템이 중단될 위험이 있다. 클러스터링을 통해 여러 브로커가 부하를 분산하고, 한 브로커가 실패해도 다른 브로커가 작업을 이어받을 수 있다. 파티셔닝은 메시지를 여러 브로커에 분산 저장하는 방법이다. 각 토픽을 여러 파티션으로 나누고, 각 파티션을 서로 다른 브로커가 관리한다. 메시지의 키를 해시하여 파티션을 결정하므로, 같은 키를 가진 메시지는 항상 같은 파티션으로 전송되어 순서가 보장된다. 복제는 각 파티션의 복사본을 여러 브로커에 유지하는 방법이다. 주 복제본이 모든 읽기/쓰기를 처리하고, 보조 복제본은 주 복제본과 동기화를 유지하다가 장애 시 주 복제본 역할을 인수한다.
RabbitMQ, Apache Kafka, Redis Pub/Sub 등 다양한 메시지 브로커가 각자의 특성에 맞게 이러한 개념들을 구현하고 있다. RabbitMQ는 AMQP 프로토콜을 기반으로 복잡한 라우팅과 다양한 전달 보장 수준을 제공한다. Kafka는 대용량 스트리밍 데이터 처리에 최적화되어 있으며, 로그 기반 저장 구조로 높은 처리량을 달성한다. Redis는 인메모리 데이터 구조를 활용하여 초저지연 메시징을 제공한다. Celery는 이러한 다양한 브로커를 백엔드로 사용할 수 있어, 애플리케이션의 요구사항에 맞는 브로커를 선택할 수 있는 유연성을 제공한다. 메시지 브로커는 현대 분산 시스템의 신경계와 같은 역할을 하며, 느슨하게 결합된 컴포넌트들이 안정적이고 확장 가능한 방식으로 통신할 수 있게 하는 핵심 인프라다.