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

Artemis의 'Large Message'가 메모리가 아닌 디스크 폴더에 저장되는 임계값은?

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

Artemis의 'Large Message'가 메모리가 아닌 디스크 폴더에 저장되는 임계값은?

엔터프라이즈 메시징 시스템을 설계할 때 텍스트 기반의 가벼운 JSON 데이터만 오가는 것은 아닙니다. 때로는 수십 MB에 달하는 첨부 파일, 수 GB의 로그 파일, 혹은 거대한 배치(Batch) 데이터를 브로커를 통해 비동기로 전달해야 하는 비즈니스 요구사항이 빈번하게 발생합니다.

하지만 일반적인 메시지처럼 이 거대한 데이터를 브로커의 JVM 힙(Heap) 메모리에 모두 올려서 처리하려 한다면, 브로커는 순식간에 OOM(Out of Memory) 장애를 일으키며 시스템 전체가 붕괴됩니다. 차세대 브로커인 ActiveMQ Artemis는 이러한 대용량 페이로드를 힙 메모리에 적재하지 않고 물리적 디스크로 직접 우회시키는 'Large Message(대용량 메시지)' 스트리밍 아키텍처를 내장하고 있습니다.

본 가이드에서는 일반 메시지가 Large Message로 취급되어 디스크로 직행하게 되는 정확한 임계값(Threshold) 설정과, 그 이면에서 동작하는 브로커의 스토리지 최적화 메커니즘을 상세히 해부합니다.


1. 메모리 우회: Large Message의 방어 아키텍처

일반적인 메시지는 네트워크를 통해 브로커에 인입되면 먼저 JVM 힙 메모리에 완전한 객체 형태로 적재됩니다. 이후 라우팅 엔진을 거쳐 디스크의 저널(Journal) 파일에 영속화되는 과정을 거칩니다.

반면, 브로커가 특정 메시지를 'Large Message'로 판별하는 순간 처리 파이프라인이 완전히 바뀝니다. 브로커는 이 메시지의 본문(Payload)을 메모리에 담지 않고, 네트워크 소켓에서 바이트 스트림을 읽어 들이는 즉시 운영체제의 파일 시스템(디스크)으로 스트리밍하여 직접 기록합니다.

결과적으로 브로커의 메모리에는 이 거대한 데이터가 디스크 어디에 저장되어 있는지 가리키는 아주 작은 크기의 메타데이터(포인터)만 남게 됩니다. 이 메커니즘 덕분에 아무리 큰 파일이 수백 개 유입되더라도 브로커의 힙 메모리 고갈을 완벽하게 방어할 수 있습니다.


2. 디스크 전환의 절대적 기준점: 'min-large-message-size'

그렇다면 브로커는 메시지 크기가 몇 바이트부터 대용량 메시지로 간주하고 디스크 스트리밍을 시작할까요? 이를 결정하는 핵심 임계값 설정이 바로 min-large-message-size입니다.

  • 기본 임계값: Artemis의 기본 설정값은 100KB (102400 바이트)입니다. 즉, 프로듀서가 보낸 메시지의 페이로드 크기가 100KB를 단 1바이트라도 초과하는 순간, 해당 메시지는 무조건 메모리를 우회하여 디스크 폴더로 직행합니다.
  • 설정 방법: 이 임계값은 브로커와 클라이언트가 연결되는 접점인 커넥터(Acceptor) 설정에서 직접 제어할 수 있습니다. broker.xml 파일 내 <acceptors> 블록에서 파라미터로 지정합니다.
<acceptors>
   <acceptor name="artemis">tcp://0.0.0.0:61616?minLargeMessageSize=1048576</acceptor>
</acceptors>

3. 물리적 저장소: 'large-messages' 디렉토리의 생명주기

임계값을 넘어선 대용량 메시지들은 일반 메시지들이 뭉쳐서 저장되는 고속 순차 저널 파일(db-*.log 등)이나 페이징(Paging) 파일에 섞이지 않습니다.

  • 독립된 스토리지: 브로커 설정 파일의 <large-messages-directory> 요소에 지정된 별도의 물리적 폴더(기본값은 브로커 데이터 폴더 하위의 large-messages 디렉토리)에 개별적인 물리 파일 형태로 격리되어 저장됩니다.
  • 파일 포맷: 해당 디렉토리를 열어보면 메시지의 내부 고유 ID를 파일명으로 하는 확장자 없는 거대한 파일들(예: 1234567890.msg)이 생성되어 있는 것을 확인할 수 있습니다. 하나의 메시지가 곧 하나의 독립된 파일로 1:1 매핑됩니다.
  • 가비지 컬렉션(GC): 이 거대한 파일들은 컨슈머가 메시지를 완전히 다운로드하고 정상적으로 처리 완료(ACK) 신호를 브로커에 보내는 즉시 파일 시스템에서 영구적으로 삭제(Delete)됩니다. 이를 통해 디스크 공간이 낭비되는 것을 방지합니다.

4. 클라이언트 단의 스트리밍(Streaming) 처리 기법

브로커가 아무리 디스크로 스트리밍하여 메모리를 아낀다 하더라도, 클라이언트 애플리케이션의 구현이 잘못되면 무용지물입니다. 메시지를 보내는 프로듀서나 받는 컨슈머 애플리케이션이 1GB짜리 파일을 자바의 바이트 배열(Byte Array) 버퍼에 통째로 올려서 통신하려 한다면, 결국 클라이언트 쪽의 웹 서버나 배치 서버가 OOM으로 먼저 뻗어버립니다.

이를 막기 위해 Artemis 클라이언트 API는 InputStreamOutputStream을 통한 파일 청크(Chunk) 단위의 스트리밍 입출력을 네이티브로 지원합니다.
프로듀서는 로컬 디스크의 대용량 파일을 InputStream으로 열어 브로커로 조금씩 흘려보내야 하며, 컨슈머 역시 OutputStream을 통해 네트워크에서 들어오는 스트림 데이터를 메모리에 담아두지 않고 즉시 로컬 디스크로 써 내려가야 합니다. 이렇게 엔드투엔드(End-to-End) 스트리밍 파이프라인을 구축해야만 진정한 의미의 대용량 메시지 처리가 완성됩니다.


5. 아키텍처 설계 시의 트레이드오프와 튜닝 전략

OOM을 막기 위해 min-large-message-size를 무조건 작게 설정하는 것이 능사는 아닙니다. 임계값을 낮출수록 JVM 힙 메모리는 극도로 안전해지지만, 그만큼 디스크 I/O 병목이라는 혹독한 대가를 치러야 합니다.

  • 임계값이 너무 낮을 때 (예: 10KB): 시스템에 들어오는 거의 모든 텍스트 메시지가 별도의 물리적 파일 생성 및 독립적인 디스크 쓰기 작업을 유발합니다. 일반 저널 방식의 장점인 고속 일괄 쓰기(Batching)의 이점을 모두 잃어버리고, 무수한 무작위 파일 I/O 오버헤드로 인해 브로커의 전체 초당 처리량(TPS)이 바닥으로 곤두박질칩니다.
  • 임계값이 너무 높을 때 (예: 50MB): 40MB짜리 꽤 무거운 메시지들이 대용량 메시지로 취급되지 않고 모조리 브로커의 힙 메모리로 올라옵니다. 브로커의 가비지 컬렉터(GC)가 수시로 멈춤 현상(Stop-The-World)을 유발하거나, 트래픽 피크 타임에 브로커가 메모리 한계를 버티지 못하고 즉각 강제 종료됩니다.
  • 설정 모범 사례 (Best Practice): 인프라가 다루는 비즈니스 메시지의 평균 크기를 철저히 모니터링하십시오. 전체 트래픽의 95% 이상을 차지하는 일반적인 단문 메시지는 메모리에서 초고속으로 처리될 수 있도록 임계값을 그보다 약간 높게 설정하고, 어쩌다 한 번씩 들어오는 거대한 파일 데이터만 디스크 파일로 빠지도록 밸런스를 맞추는 것이 인프라 튜닝의 핵심입니다.

결론적으로 메시지 브로커는 본질적으로 거대한 파일을 쌓아두는 전용 파일 서버(FTP)나 오브젝트 스토리지가 아닙니다. Large Message 기능은 시스템 간 결합도를 낮추기 위해 어쩔 수 없이 대용량 파일을 큐로 넘겨야 할 때, 브로커의 붕괴를 막기 위한 훌륭한 안전장치로 전략적으로 활용해야 합니다.

반응형