본문 바로가기
1. 개발/1.8. ActiveMQ

'decreaseNetworkConsumerPriority' 옵션의 클러스터 부하 분산 원리는?

by 엉짱 2026. 4. 10.
반응형

'decreaseNetworkConsumerPriority' 옵션의 클러스터 부하 분산 원리는?

엔터프라이즈 인프라에서 메시지 브로커(ActiveMQ 등)의 한계를 돌파하기 위해 여러 대의 브로커를 엮는 'Network of Brokers (NoB)' 아키텍처를 구축하는 궁극적인 목적은 '확장성'과 '부하 분산(Load Balancing)'입니다. 많은 엔지니어들이 브로커들을 네트워크로 묶어두기만 하면 메시지가 마법처럼 골고루 분산 처리될 것이라고 기대합니다.

하지만 브로커 내부의 디스패치(Dispatch) 엔진을 정교하게 통제하지 않으면, 부하 분산은커녕 끔찍한 네트워크 대역폭 낭비와 지연 시간(Latency) 증가라는 부메랑을 맞게 됩니다. 이러한 분산 아키텍처의 비효율성을 원천 차단하고, 가장 이상적인 형태의 지능형 부하 분산을 완성하는 핵심 스위치가 바로 decreaseNetworkConsumerPriority 옵션입니다.

이 가이드에서는 클러스터 내에서 브릿지(Bridge)가 유발하는 라우팅의 함정을 해부하고, 이 옵션이 어떻게 시스템을 '스필 오버(Spill-over)' 형태의 완벽한 분산 파이프라인으로 탈바꿈시키는지 상세히 분석합니다.


1. 단순 라운드 로빈(Round-Robin) 방식의 치명적 함정

decreaseNetworkConsumerPriority의 원리를 이해하려면, 먼저 이 옵션이 꺼져 있을 때(false) 인프라 내부에 어떤 재앙이 발생하는지 살펴보아야 합니다.

A 브로커와 B 브로커가 네트워크 브릿지로 연결되어 있고, 각 브로커에 메시지를 처리하는 컨슈머 애플리케이션이 하나씩 붙어있다고 가정해 보겠습니다. 프로듀서가 A 브로커로 100개의 메시지를 쏟아냅니다.

  • 브릿지의 정체: A 브로커 입장에서 볼 때, A 브로커에 직접 붙은 로컬 컨슈머나, 브릿지를 타고 연결된 B 브로커의 네트워크 컨슈머나 모두 동일한 자격의 '구독자(Subscriber)'로 보입니다.
  • 멍청한 균등 분배: 기본적으로 브로커는 메시지를 라운드 로빈 방식으로 분배합니다. 1번 메시지는 A 로컬 컨슈머로, 2번 메시지는 브릿지를 통해 B 브로커로, 3번 메시지는 다시 A로 보냅니다.
  • 아키텍처의 비극: A 브로커의 로컬 컨슈머가 100개의 메시지를 순식간에 처리할 수 있는 충분한 CPU와 메모리를 가지고 있음에도 불구하고, 브로커는 굳이 50개의 메시지를 무거운 TCP 네트워크(브릿지)를 태워 B 브로커로 넘겨버립니다. 불필요한 네트워크 홉(Hop)이 발생하며 전반적인 메시지 처리 지연 시간이 2배 이상 길어지고 백본 네트워크 대역폭이 낭비됩니다.

2. 로컬 우선주의의 확립: 우선순위(Priority) 강등 메커니즘

위와 같은 '불필요한 횡단'을 막기 위한 ActiveMQ의 해답이 바로 decreaseNetworkConsumerPriority="true" (최신 버전 기본값) 옵션입니다. 이 옵션은 브로커 내부의 디스패치 우선순위(Dispatch Priority) 점수를 강제로 조작하여 트래픽의 흐름을 통제합니다.

A. 동작 원리 (우선순위 스코어링)
ActiveMQ 내부에서 모든 컨슈머는 메시지를 받을 자격을 나타내는 0부터 127까지의 우선순위 값을 가집니다. 기본값은 0(가장 높음)입니다.

  • A 브로커에 직접 붙은 '로컬 애플리케이션 컨슈머'는 기본 우선순위인 0을 부여받습니다.
  • 이 옵션이 활성화되면, B 브로커로 연결되는 '네트워크 브릿지 컨슈머'는 우선순위가 강제로 깎여(Decrease) 보통 -5 (기본 페널티 값)의 낮은 우선순위를 부여받게 됩니다.

B. 라우팅의 변화
브로커의 디스패치 엔진은 큐에 메시지가 들어오면 무조건 우선순위가 높은 컨슈머에게 먼저 메시지를 밀어 넣습니다.
따라서 프로듀서가 A 브로커에 100개의 메시지를 보내면, 브릿지(우선순위 -5)는 철저히 무시당하고 로컬 컨슈머(우선순위 0)가 100개의 메시지를 100% 독식하여 처리하게 됩니다. 네트워크를 건너는 오버헤드가 완벽하게 소멸하는 것입니다.


3. 진정한 부하 분산: 스필 오버(Spill-over) 아키텍처의 완성

그렇다면 여기서 한 가지 날카로운 의문이 생깁니다. *"로컬 컨슈머가 다 가져가 버리면, B 브로커는 평생 놀고 있는 것 아닌가? 그러면 부하 분산(Load Balancing)이 아니지 않은가?"*

이 의문을 해결하는 핵심 키워드가 바로 클라이언트의 '프리패치 버퍼(Prefetch Buffer)'입니다. decreaseNetworkConsumerPriority는 프리패치 버퍼와 결합할 때 진정한 의미의 '스필 오버(넘쳐흐름)' 부하 분산을 완성합니다.

  1. 로컬의 한계 도달: 로컬 컨슈머가 한 번에 가져갈 수 있는 프리패치 사이즈가 1000개라고 가정해 봅시다. 트래픽이 폭주하여 A 브로커에 5000개의 메시지가 순식간에 쏟아졌습니다.
  2. 로컬 컨슈머의 포화: 로컬 컨슈머는 1000개의 메시지를 가져가서 열심히 처리하고 있습니다. 이제 로컬 컨슈머의 프리패치 버퍼는 꽉 찼으며(Full), 더 이상 메시지를 받을 수 없는 'Busy' 상태가 됩니다.
  3. 네트워크 브릿지의 개입 (Spill-over): A 브로커의 큐에는 아직 4000개의 메시지가 대기 중입니다. 우선순위 0인 로컬 컨슈머가 꽉 차서 메시지를 받을 수 없게 되자, 브로커 디스패치 엔진은 그제야 차순위(우선순위 -5)인 네트워크 브릿지 컨슈머에게 눈을 돌립니다.
  4. 지능형 분산: 대기 중이던 4000개의 메시지가 브릿지를 타고 B 브로커로 흘러갑니다(넘쳐흐릅니다). B 브로커에 대기 중이던 컨슈머들이 이 메시지를 받아 처리함으로써, 시스템 전체가 다운되지 않고 완벽하게 부하를 나누어 가지게 됩니다.

즉, 이 아키텍처는 "내가 감당할 수 있을 때까지는 내 선에서 최대한 빠르고 효율적으로(로컬) 처리하고, 내 한계를 벗어나서 시스템이 위험해질 때만 동료(원격 브로커)에게 도움을 요청한다"는 가장 이상적인 분산 시스템의 철학을 구현한 것입니다.


4. 깊은 토폴로지(Deep Topology)에서의 가중치 누적

브로커가 A-B-C 형태로 3대 이상 길게 엮여 있는 거대한 Mesh 네트워크나 Hub-and-Spoke 환경에서 이 옵션은 더욱 마법처럼 동작합니다.

메시지가 브릿지를 한 번 건널 때마다(Network Hop), 컨슈머의 우선순위는 누적해서 계속 깎이게 됩니다.

  • A 브로커의 로컬 컨슈머: 우선순위 0
  • B 브로커의 컨슈머 (1 Hop): 우선순위 -5
  • C 브로커의 컨슈머 (2 Hop): 우선순위 -10

이 가중치 누적 메커니즘 덕분에, A 브로커에 들어온 트래픽은 가장 가까운 A에서 먼저 처리되고, A가 바쁘면 바로 옆의 B로 넘어가며, A와 B가 모두 터질 것 같이 바빠야만 가장 멀리 있는 C 브로커까지 메시지가 전달됩니다. 물리적으로 가장 가까운 컴퓨팅 자원부터 순차적으로 알뜰하게 소모하게 만드는 아키텍트의 정교한 통제선입니다.


5. 아키텍처 튜닝 가이드 및 주의사항 (Best Practices)

이 강력한 옵션을 실무에 적용할 때는 activemq.xml<networkConnector> 블록에 설정하며, 반드시 다음의 튜닝 포인트들을 점검해야 합니다.

<networkConnectors>
    <networkConnector name="bridge-to-B" 
                      uri="static:(tcp://brokerB:61616)" 
                      decreaseNetworkConsumerPriority="true" 
                      conduitSubscriptions="true" />
</networkConnectors>

[운영 시 필수 고려사항: Prefetch 튜닝]
이 스필 오버 메커니즘이 정상적으로 작동하려면, 클라이언트 애플리케이션의 Prefetch Size가 절대적으로 통제되어야 합니다.
만약 로컬 컨슈머의 프리패치 사이즈가 100만 개(혹은 무제한)로 너무 크게 잡혀있다면, 큐에 아무리 많은 메시지가 쌓여도 로컬 컨슈머가 다 끌어안고 버티게 됩니다. 로컬 컨슈머의 버퍼가 'Full' 상태가 되지 않으므로, 네트워크 브릿지로는 영원히 메시지가 넘어가지 않아 결과적으로 B 브로커는 계속 놀게 되는 페이크(Fake) 분산 환경이 되어버립니다.
따라서 이 옵션을 도입할 때는 반드시 컨슈머의 프리패치 사이즈를 100 이하의 타이트한 수치로 조여주어, 로컬 노드가 바쁘다는 신호(Backpressure)를 브로커가 즉각적으로 감지할 수 있도록 인프라의 민감도를 높여야 합니다.

결론적으로 decreaseNetworkConsumerPriority는 단순한 설정값 하나가 아니라, 분산 시스템에서 '데이터의 지역성(Data Locality)'을 극대화하고 네트워크 횡단 비용을 통제하는 중추 신경입니다. 로컬 리소스를 한계치까지 활용한 뒤에만 원격 리소스를 호출하는 이 지능적인 부하 분산 메커니즘을 통해, 어떠한 트래픽 폭주 상황에서도 흔들림 없는 브로커 클러스터를 완성하시기 바랍니다.

반응형