3 minute read

일시적 메시징과 영구 저장소의 차이는 무엇일까. 메시지 브로커에서 흔히 볼 수 있는 일시적 메시징은 일반적으로 컨슈머에게 전달된 메시지를 삭제한다. 반면 데이터베이스와 파일시스템은 명시적으로 삭제할 때까지 데이터를 영구적으로 기록하는 것을 목표로 한다.

  • 파생 데이터에 미치는 영향: 두 시스템의 차이는 파생 데이터가 생성되는 방식에 영향을 미친다. 배치 프로세스는 입력 데이터에 대한 위험 없이 반복적인 실험을 허용하지만, AMQP/JMS 스타일의 메시징은 승인으로 인해 브로커에서 메시지가 삭제되므로 파괴적인 결과를 초래한다.
  • 컨슈머 행동: 메시징 시스템에서 새로운 컨슈머는 전송된 메시지를 수신하며, 이전 메시지는 복구할 수 없게 사라진다. 반면, 파일과 데이터베이스를 사용하면 명시적으로 덮어쓰거나 삭제하지 않는 한 새로운 클라이언트가 과거 데이터를 읽을 수 있다.
  • 하이브리드 접근 방식: 로그 기반 메시지 브로커는 데이터베이스의 내구성 있는 저장 원칙과 메시징 시스템의 지연 시간이 짧은 알림 기능을 결합한 시스템이다.

메시지 브로커에 로그를 사용하기

로그는 디스크 저장된 추가 전용(append-only) 레코드 시퀀스다.

메시지 브로커는 프로듀서가 로그에 메시지를 추가하고 컨슈머는 이를 순차적으로 읽는 로그 구조를 사용하여 구현할 수 있다. 컨슈머가 로그의 끝에 도달하면, 유닉스 도구 tail -f와 유사하게 새 메시지에 대한 알림을 기다린다.

더 높은 처리량을 달성하기 위해 로그를 파티셔닝하고 다른 머신에서 호스팅할 수 있다. 각 파티션은 읽기 및 쓰기를 위한 독립적인 로그가 된다. 토픽은 동일한 유형의 메시지를 포함하는 파티션 그룹으로 정의할 수 있다.

각 파티션 내에서 단조롭게 증가하는 시퀀스 번호 또는 오프셋이 모든 메시지에 할당되어 해당 파티션 내의 전체 순서를 유지하게 해준다. 다른 파티션 간에는 순서가 보장되지 않는다.

Apache Kafka, Amazon Kinesis Streams, Twitter의 DistributedLog, Google Cloud Pub/Sub(JMS 스타일 API 사용)는 여러 컴퓨터에 걸쳐 메시지를 분할하고 복제하여 높은 처리량과 내결함성을 달성하는 로그 기반 메시지 브로커다.

기존 메시징 시스템과 로그 기반 메시징의 차이점

로그 기반 접근 방식은 여러 컨슈머가 서로에게 영향을 주지 않고 독립적으로 로그를 읽을 수 있으므로 팬아웃 메시징을 쉽게 지원한다. 메시지를 읽어도 로그에서 삭제되지 않는다.

로그 기반 브로커는 컨슈머 그룹에서 로드 밸런싱을 달성하기 위해 개별 메시지가 아닌 전체 파티션을 노드에 할당한다. 각 클라이언트는 할당된 파티션의 모든 메시지를 단일 스레드 방식으로 순차적으로 소비한다.

작업을 공유하는 노드 수는 토픽의 로그 파티션 수로 제한된다. 단일 메시지의 처리 속도가 느리면 같은 파티션의 후속 메시지가 차단될 수 있다(헤드 오브 라인을 차단함).

로그 기반 메시징은 메시지 단위로 처리하는 데 비용이 많이 들고, 병렬 처리가 필요하다. 메시지 순서가 덜 중요한 경우에는 JMS/AMQP 스타일의 메시지 브로커를 사용하는 것이 바람직하다. 로그 기반 접근 방식은 각 메시지를 빠르게 처리해야 하고 메시지 순서가 필수적인 높은 메시지 처리량 시나리오에 적합하다.

로그 기반 메시지 브로커의 디스크 공간 사용량

로그는 세그먼트라는 단위로 나눠진다. 오래된 세그먼트는 주기적으로 삭제되거나 아카이브 스토리지로 이동되어 디스크 공간을 확보한다.

로그는 효과적으로 크기가 제한된 버퍼로 작동하며, 이 버퍼가 가득 차면 오래된 메시지를 삭제한다. 이 동작은 원형 버퍼 또는 링 버퍼와 유사하지만 디스크에 저장되기 때문에 상당히 클 수 있다.

대략적인 계산에 따르면 용량이 6TB이고 순차 쓰기 처리량이 150MB/s인 일반적인 대용량 하드 드라이브는 오래된 메시지를 덮어쓰기 전에 약 11시간 분량의 메시지를 버퍼링할 수 있다. 하드 드라이브와 머신이 여러 대인 경우에도 이 비율은 비슷하게 유지된다. 실제로 배포에는 며칠 또는 몇 주 분량의 메시지가 버퍼링되는 경우가 많다.

로그는 메시지 보존 기간에 관계없이 모든 메시지가 디스크에 기록되므로 비교적 일정한 처리량을 유지한다. 이는 메시지를 주로 메모리에 보관하고 대기열이 너무 커질 때만 디스크에 기록하는 메시징 시스템과 대조적이며, 그 결과 보존된 기록에 따라 처리량이 달라진다.

컨슈머가 프로듀서의 속도를 따라잡을 수 없을 때 해결책

다음은 로그 기반 접근 방식에서 컨슈머가 프로듀서의 메시징 전송 속도를 따라잡을 수 없을 때 해결책이다.

  • 버퍼링: 로그 기반 접근 방식은 버퍼링의 한 형태다. 사용 가능한 디스크 공간에 따라 제한되는 큰 크기의 고정 버퍼를 활용하여 메시지를 수용한다.
  • 컨슈머 지연 처리: 컨슈머의 지연 시간이 너무 길어 필요한 메시지가 디스크에 보관된 것보다 오래된 경우, 오래된 메시지를 읽을 수 없다. 브로커는 버퍼의 용량을 초과하는 오래된 메시지를 삭제한다. 운영자는 컨슈머의 지연을 모니터링하고 경고를 발생시킴으로써 메시지 누락이 문제가 되기 전에 느린 컨슈머 문제를 해결할 수 있다.
  • 느린 컨슈머 격리: 한 컨슈머가 너무 뒤처져 메시지가 누락되기 시작하면 해당 특정 컨슈머에게만 영향을 미친다. 이러한 운영상의 이점 덕분에 프로덕션 서비스를 중단하지 않고도 개발, 테스트 또는 디버깅을 위해 프로덕션 로그를 안전하게 실험적으로 사용할 수 있다.

오래된 메시지를 재생하기

로그 기반 메시지 브로커가 일반적인 메시지 브로커에 비해 오래된 메시지를 재생하는 데 유리한 이유는 무엇일까?

  • 비파괴적 소비: 로그 기반 메시지 브로커에서 메시지를 소비하는 것은 파일에서 읽는 것과 유사하며, 로그를 변경하지 않는 읽기 전용 작업이다. 메시지 처리 및 확인이 파괴적인 작업인 AMQP 및 JMS 스타일 브로커와 달리, 로그 기반 브로커는 소비 후에도 메시지를 보존한다.
  • 컨슈머 오프셋 제어: 컨슈머는 오프셋을 조작하여 메시지를 쉽게 재생할 수 있다. 예를 들어, 컨슈머는 이전 날의 오프셋으로 시작할 수 있으며, 출력을 다른 위치에 기록하여 과거 메시지를 재처리할 수 있다. 이러한 유연성 덕분에 처리 코드의 반복적인 실험과 변형이 가능하다.
  • 배치 프로세싱: 로그 기반 메시징은 반복 가능한 변환 프로세스를 통해 파생된 데이터를 입력 데이터에서 분리하는 배치 프로세스에 비유할 수 있다. 이러한 특성은 조직 내에서 실험, 오류 복구 및 데이터 흐름 통합을 용이하게 한다.