'Strict Order Dispatching' 옵션이 전체 처리량에 미치는 영향은?
엔터프라이즈 환경에서 메시지 브로커(ActiveMQ 등)를 도입하는 가장 큰 목적은 비동기 처리를 통해 시스템의 확장성(Scalability)과 전체 처리량(Throughput)을 극대화하는 것입니다. 수많은 워커(Worker) 노드를 붙여 데이터를 병렬로 처리하면 초당 수만 건의 트래픽도 거뜬히 소화할 수 있습니다.
하지만 비즈니스 현장에서는 종종 "결제 요청과 결제 취소는 반드시 발생한 순서대로 처리되어야 한다"는 엄격한 요구사항에 직면하게 됩니다. 이를 해결하기 위해 브로커 설정에서 무심코 켜게 되는 옵션이 바로 'Strict Order Dispatching(엄격한 순서 분배)'입니다.
이 옵션은 데이터의 순서를 완벽하게 보장해주지만, 시스템의 전체 성능과 아키텍처의 근간을 뒤흔들 수 있는 양날의 검입니다. 이 가이드에서는 해당 옵션의 동작 원리와 전체 처리량에 미치는 치명적인 영향, 그리고 이를 극복하기 위한 대안 아키텍처를 상세히 분석합니다.

1. 분산 환경에서 메시지 순서 보장의 딜레마
기본적으로 메시지 브로커는 큐(Queue)에 쌓인 메시지를 컨슈머(Consumer)들에게 최대한 빠르고 공평하게 나누어주기 위해 라운드 로빈(Round-Robin)이나 로드 밸런싱 알고리즘을 사용합니다.
메시지 A, B, C가 순서대로 큐에 들어왔다고 가정해 보겠습니다.
브로커가 이를 세 대의 컨슈머 서버 1, 2, 3에 각각 동시에 분배합니다. 만약 컨슈머 1이 가비지 컬렉션(GC)으로 인해 잠시 멈칫한다면, 나중에 들어온 메시지 B와 C가 메시지 A보다 먼저 데이터베이스에 반영되는 '순서 역전' 현상이 발생합니다.
이러한 분산 환경의 태생적인 한계를 시스템 레벨에서 강제로 통제하여, 메시지 A가 완전히 처리될 때까지 B와 C의 처리를 막는 것이 바로 'Strict Order Dispatching'의 핵심입니다.
2. 'Strict Order Dispatching'의 동작 원리와 설정
ActiveMQ Classic 환경에서는 activemq.xml의 목적지 정책 설정 블록에서 이 기능을 활성화할 수 있습니다. 주로 하나의 토픽(Topic)이나 큐에 여러 컨슈머가 붙어 있을 때, 메시지가 섞이는 것을 방지하기 위해 디스패치 정책(Dispatch Policy)을 변경합니다.
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue=">">
<dispatchPolicy>
<strictOrderDispatchPolicy />
</dispatchPolicy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
이 옵션이 켜지면 브로커의 내부 디스패처 스레드는 큐에 들어온 메시지의 인덱스를 엄격하게 추적합니다. 여러 컨슈머가 연결되어 있더라도, 브로커는 내부적으로 메시지의 순서를 보장하기 위해 한 번에 하나의 컨슈머에게만 순차적으로 메시지를 밀어 넣거나, 다중 전송을 극도로 제한하는 동기화(Synchronization) 락을 걸게 됩니다.
3. 전체 처리량(Throughput)에 미치는 치명적 영향
순서를 보장한다는 것은 곧 "앞선 작업이 끝나기를 기다려야 한다"는 것을 의미합니다. 이 옵션을 프로덕션 환경에 적용하는 순간, 전체 시스템의 처리량(TPS)은 곤두박질치게 됩니다.
A. 동시성(Concurrency)과 스케일 아웃의 무력화
메시징 시스템의 가장 큰 장점은 컨슈머 인스턴스를 10대, 100대로 늘려(Scale-out) 병렬 처리 속도를 높이는 것입니다. 하지만 엄격한 순서 보장을 강제하면, 아무리 컨슈머 노드를 많이 붙여도 브로커는 순서를 맞추기 위해 사실상 단일 스레드(Single-thread)처럼 동작하게 됩니다. 100대의 서버 중 99대는 놀고 있고, 1대만 열심히 일하는 병목 현상이 발생합니다.
B. 선두 차단 현상 (Head-of-Line Blocking)
큐의 가장 앞에 있는 메시지를 처리하는 컨슈머 측 비즈니스 로직에 외부 API 지연이나 DB 락(Lock)이 발생하여 처리가 10초간 지연되었다고 가정해 봅니다. Strict Order 환경에서는 이 10초 동안 뒤에 쌓여 있는 수만 개의 독립적인 정상 메시지들조차 전혀 분배되지 못하고 큐에 그대로 갇히게 됩니다. 하나의 불량 메시지가 전체 시스템을 마비시키는 것입니다.
C. 브로커의 CPU 및 메모리 오버헤드 급증
브로커는 단순히 메시지를 전달하는 것을 넘어, 어떤 컨슈머에게 몇 번째 메시지까지 전달했는지, 순서가 꼬이지는 않았는지 끊임없이 상태를 추적하고 내부적인 동기화 블록을 유지해야 합니다. 이는 메인 라우팅 엔진에 심각한 오버헤드를 유발하며 전체 브로커의 응답 지연을 초래합니다.
4. Strict Order가 꼭 필요한 비즈니스 시나리오
그렇다면 이 옵션은 언제 사용해야 할까요? 성능 저하를 감수하고서라도 데이터의 '상태 변화 이력'이 절대적으로 중요한 도메인에서만 극히 제한적으로 사용해야 합니다.
- 데이터베이스 복제 로그 (CDC - Change Data Capture): 마스터 DB의 변경 사항(Insert -> Update -> Delete)을 타겟 DB로 스트리밍할 때, 순서가 하나라도 뒤바뀌면 데이터 정합성이 영구적으로 붕괴됩니다.
- 주식 호가 시스템: 매수와 매도 주문의 미세한 시간차와 체결 순서가 비즈니스의 핵심 가치인 경우.
위와 같은 특수한 경우가 아니라면, 일반적인 알림 발송이나 로그 수집 시스템에서는 절대 사용해서는 안 되는 안티 패턴(Anti-Pattern)입니다.
5. 대안 아키텍처: 순서와 성능의 타협점 (Message Groups)
전체 큐의 병렬 처리량을 훼손하지 않으면서도, "특정 사용자"나 "특정 주문 건"에 대해서만 순서를 보장하고 싶을 때 사용하는 가장 완벽한 대안이 바로 Message Groups (JMSXGroupID) 기능입니다.
앞선 포스팅에서 다루었던 것처럼, 생산자가 메시지를 보낼 때 주문 번호(예: ORDER-123)를 JMSXGroupID 헤더에 세팅합니다.
브로커는 "전체 메시지"의 순서를 강제하는 대신, "동일한 그룹 ID를 가진 메시지"들만 오직 하나의 동일한 컨슈머에게 순서대로 전달합니다.
- 사용자 A의 결제 데이터는 컨슈머 1번이 순서대로 처리하고,
- 사용자 B의 결제 데이터는 컨슈머 2번이 순서대로 병렬 처리합니다.
이 방식을 채택하면 Strict Order Dispatching의 치명적인 병목 현상을 우회하면서도 비즈니스 로직이 요구하는 순서 정합성을 완벽하게 지켜낼 수 있습니다.
'1. 개발 > 1.8. ActiveMQ' 카테고리의 다른 글
| 'Conduit Subscriptions' 옵션이 클러스터 중복 수신을 막는 원리는? (0) | 2026.03.21 |
|---|---|
| 브로커 간 연결 시 'Duplex' 옵션의 장단점은? (0) | 2026.03.21 |
| 브로커가 프로듀서를 차단하는 'Usage Manager'의 임계치 설정법은? (0) | 2026.03.20 |
| 'Consumer Window Size'와 네트워크 대역폭의 관계는? (0) | 2026.03.20 |
| 'Producer Window Size'를 통한 클라이언트 흐름 제어 원리는? (0) | 2026.03.20 |