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

브로커의 'Global Max Size' 도달 시 메시지 처리 거부 방식은?

by 엉짱 2026. 3. 21.
반응형

브로커의 'Global Max Size' 도달 시 메시지 처리 거부 방식은?

엔터프라이즈 환경에서 메시지 브로커를 운영할 때 직면하는 가장 치명적인 장애는 JVM 힙(Heap) 메모리 고갈로 인한 브로커 전체의 다운(Out of Memory, OOM)입니다. 수많은 프로듀서(Producer)가 트래픽을 쏟아내고 컨슈머(Consumer)의 처리가 지연되는 상황에서, 브로커는 스스로를 보호하기 위해 최후의 방어선을 구축해야 합니다.

ActiveMQ Artemis에서 이 최후의 방어벽 역할을 하는 핵심 설정이 바로 global-max-size입니다. 개별 큐나 토픽의 한계를 넘어, 브로커 인스턴스 전체가 메시지 적재를 위해 사용할 수 있는 메모리의 절대적인 상한선을 의미합니다.

이 가이드에서는 브로커의 메모리 사용량이 이 글로벌 임계치에 도달했을 때, 내부적으로 어떤 메커니즘을 통해 메시지 처리를 거부하고 시스템을 방어하는지 상세히 분석합니다.


1. 'Global Max Size'의 개념과 아키텍처적 위치

메시지 브로커의 메모리 관리는 크게 두 가지 계층으로 나뉩니다.

  • 주소 레벨 (Address Level): 특정 큐나 토픽 하나가 사용할 수 있는 메모리 한계(max-size-bytes)입니다.
  • 글로벌 레벨 (Global Level): 브로커에 존재하는 모든 큐와 토픽의 메모리 사용량을 합친 총량의 한계(global-max-size)입니다.

개별 큐의 한계치에 도달하지 않았더라도, 수백 개의 큐에 조금씩 쌓인 메시지의 총합이 global-max-size에 도달하면 브로커는 즉시 글로벌 차단(Global Block) 상태로 돌입합니다. 이는 배의 특정 격실에 물이 다 차지 않았더라도, 배 전체의 허용 적재량을 초과하면 배가 가라앉는 것을 막기 위해 화물 반입을 전면 중단하는 것과 같습니다.


2. 임계치 도달 시의 메시지 거부 및 차단 메커니즘

global-max-size에 도달했을 때 브로커가 취하는 행동은 매우 빠르고 단호합니다. 핵심은 "네트워크 계층에서의 백프레셔(Backpressure) 유발"입니다.

A. 클라이언트 네트워크 읽기 중단 (TCP Backpressure)
브로커 메모리가 글로벌 임계치를 치는 순간, Artemis의 네트워크 계층(Netty 기반)은 프로듀서들이 연결된 소켓(Socket)으로부터 더 이상 데이터를 읽어 들이지 않습니다.
브로커가 데이터를 읽지 않으면 운영체제의 수신 버퍼가 가득 차게 되고, 이는 곧바로 TCP 슬라이딩 윈도우 크기를 0으로 만들어 프로듀서 측의 송신 버퍼를 막아버립니다. 결과적으로 프로듀서 애플리케이션이 메시지를 전송하려 할 때 네트워크 레벨에서 블로킹(Blocking)이 발생하게 됩니다.

B. 생산자 크레딧(Producer Credit) 발급 중단
Artemis는 AMQP 1.0 프로토콜과 코어 프로토콜 등에서 흐름 제어를 위해 '크레딧(Credit)' 시스템을 사용합니다. 프로듀서는 브로커로부터 메시지를 보낼 수 있는 크레딧을 발급받아야만 전송이 가능합니다.
글로벌 메모리 한계에 도달하면 브로커는 더 이상 크레딧을 발급하지 않습니다. 크레딧이 고갈된 프로듀서는 send() 메서드 호출 시 브로커로부터 새로운 크레딧이 올 때까지 스레드 대기 상태에 빠지며, 물리적으로 트래픽 인입이 차단됩니다.

C. 예외 발생 (비동기 전송 시)
설정에 따라 블로킹 대신 에러를 반환하기도 합니다. 특정 클라이언트가 블로킹 타임아웃을 설정했거나 예외 발생 정책을 사용하는 경우, 글로벌 차단 상태에서 메시지 전송을 시도하면 즉각적으로 ActiveMQException (예: Broker is full)이 발생하며 메시지 처리가 명시적으로 거부됩니다.


3. Address 설정과의 충돌 및 우선순위

아키텍처 설계 시 가장 주의해야 할 점은 글로벌 설정이 개별 주소 설정보다 무조건 우선한다는 것입니다.

예를 들어 특정 큐의 address-full-policy를 메모리가 꽉 찼을 때 디스크로 밀어내는 PAGE 모드로 설정해 두었다고 가정해 보겠습니다.
해당 큐의 메모리는 여유가 있지만 브로커 전체의 global-max-size가 먼저 한계에 도달해 버린다면, 브로커는 해당 큐에 대한 PAGE 작업을 수행하기 전에 글로벌 차단 메커니즘을 먼저 발동시킵니다. 즉, 디스크 스왑을 시도하기도 전에 프로듀서의 연결이 멈춰버리는 현상이 발생합니다.

따라서 각 주소별 max-size-bytes의 총합이 global-max-size를 훌쩍 넘기지 않도록, 페이징 정책과 메모리 한계치를 논리적으로 일치시키는 정교한 용량 산정(Capacity Planning)이 필수적입니다.


4. 실무 설정 가이드 및 아키텍처 방어 전략

broker.xml 파일에서 글로벌 메모리를 제어하는 설정은 다음과 같습니다.

<core xmlns="urn:activemq:core">
   <global-max-size>2147483648</global-max-size>
</core>

튜닝 및 설계 모범 사례:

  1. JVM 힙 메모리의 50% 법칙: global-max-size를 명시적으로 설정하지 않으면, Artemis는 구동 시 JVM 최대 힙 메모리(-Xmx)의 정확히 절반(50%)을 기본값으로 할당합니다. 나머지 50%는 브로커의 내부 동작, 네트워크 버퍼, 관리 스레드 등을 위해 반드시 비워두어야 하기 때문입니다. 이 비율을 인위적으로 70% 이상으로 올리면 브로커가 OOM 에러를 발생시키며 비정상 종료될 확률이 매우 높아집니다.
  2. 프로듀서 측 서킷 브레이커 도입: 글로벌 차단이 발생하여 프로듀서의 send() 스레드가 블로킹되면, 이를 호출하는 백엔드 애플리케이션(예: 주문 웹 서버)의 스레드 풀까지 순식간에 고갈됩니다. 클라이언트 코드에서는 반드시 메시지 전송 시 적절한 타임아웃을 설정하고, 타임아웃 발생 시 서킷 브레이커(Circuit Breaker)를 열어 메시지를 임시로 로컬 DB에 저장하거나 사용자에게 실패를 빠르게 알리는(Fail-fast) 방어 로직을 구축해야 합니다.
  3. 임계치 경고 모니터링: global-max-size 도달은 시스템의 심각한 적색경보입니다. 100%에 도달하여 시스템이 멈추기 전에, JMX 메트릭을 통해 메모리 사용률이 80%를 넘어서는 순간 인프라 담당자에게 슬랙(Slack) 등의 알림이 발송되도록 모니터링 파이프라인을 구축해야 합니다.

브로커의 'Global Max Size'는 인프라의 완전한 붕괴를 막기 위해 스스로 트래픽의 혈관을 막아버리는 최후의 생명 유지 장치입니다. 이 임계치의 동작 원리를 정확히 이해하고 클라이언트와 서버 간의 백프레셔 아키텍처를 설계하시길 바랍니다.

반응형