Kafka vs Pulsar 비교 분석 ("Apache Pulsar in Action" 챕터 1.4)
본 포스트는 “Manning Publications”에서 출판하고 현재 StreamNative 에서 무료 배포 중인 책 “Apache Pulsar in Action”의 내용을 정리한 내용입니다. 본 포스트는 비상업적이며 학습 목적으로 작성되었습니다. 전문적인 내용은 반드시 원서를 참조하시기 바랍니다. 모든 저작권은 해당 출판사와 저자에게 있습니다.
링크:
1. 원서(출판사) : https://www.manning.com/books/apache-pulsar-in-action
2. 원서(배포본) : https://streamnative.io/ebooks/get-your-free-copy-of-mannings-apache-pulsar-in-action
1.4 Comparison to Apache Kafka
Apache Kafka와 Apache Pulsar는 모두 분산 메시징 시스템으로, 비슷한 메시징 개념을 가지고 있습니다. 클라이언트는 두 시스템 모두 무제한으로 데이터를 추가할 수 있는 스트림으로 논리적으로 처리되는 토픽을 통해 상호 작용합니다. 그러나 확장성(scalability), 메시지 소비(message consumption), 데이터 내구성(data durability), 메시지 보존(message retention) 등에 있어서 Apache Pulsar와 Apache Kafka 사이에는 몇 가지 기본적인 차이점이 있습니다.
1.4.1 Multilayered architecture
Apache Pulsar의 멀티레이어드 구조는 메시지 서빙 레이어와 메시지 스토리지 레이어을 완전히 분리(decouple)하여 각자 독립적으로 확장할 수 있게 합니다. Kafka와 같은 전통적인 분산 메시징 기술은 데이터 처리와 데이터 저장을 같은 클러스터 노드 또는 인스턴스에 함께 위치시키는 방식을 채택하였습니다. 이러한 선택은 더욱 심플한 인프라를 제공하고 네트워크 트래픽을 줄이는 등의 일부 성능 이점을 제공하지만 확장성, 탄력성(resiliency) 등 운영 많은 영향을 미치는 부분들을 희생하게 됩니다.
Pulsar의 아키텍처는 “컴퓨팅과 저장의 분리(seperation of compute and storage)” 라는 매우 다른 접근 방식을 취하고 있습니다. 이 방식은 최근들어 많은 클라우드 네이티브 솔루션에서 수용하고 있으며 오늘날의 큰 네트워크 대역폭 향상 덕분에 가능해진 부분입니다. Pulsar의 아키텍처는 데이터 서빙과 데이터 스토리지를 별도의 계층으로 분리합니다: 데이터 서빙은 상태가 없는 “브로커 노드(broker nodes)”에 의해 처리되며, 데이터 스토리지는 “북키퍼 노드(bookie nodes)”에 의해 처리됩니다. Figure 1.16에서 보여지는 것과 같습니다. 이러한 분리는 동적 확장성(dynamic scalability), 무중단 업그레이드, 무한한 저장 공간 확장 등 여러 가지 이점을 제공합니다. 또한, 이 설계는 컨테이너 친화적(container-friendly)이므로 Pulsar는 클라우드 네이티브 스트리밍 시스템을 호스팅하기 위한 최적의 기술이 됩니다.
DYNAMIC SCALING
다음 시나리오를 생각해 보겠습니다. CPU 사용이 많은 서비스가 있고, 요청의 규모가 특정 임계치 초과하면 성능이 저하되는 경우입니다. 기존 머신들의 CPU 사용량이 90% 이상으로 올라가는 경우 부하를 분산시키기 위해 새로운 머신와 애플리케이션 인스턴스를 제공하며 서비스를 수평적으로 확장해야 합니다. 이 과정을 모니터링 도구에 의존하여 상황을 DevOps 팀에 알리고 수동으로 작업을 수행하는 대신, 전체 과정을 자동화하는 것이 더 나은 것으로 생각됩니다.
오토스케일링은 AWS, Microsoft Azure, Google Cloud, Kubernetes와 같은 모든 퍼블릭 클라우드 제공자들이 공통적으로 지공하는 기능입니다. 오토스케일링은 CPU/메모리와 같은 리소스 사용 메트릭에 기반한 자동적인 수평적 확장을 가능하게 하며, 사람의 개입 없이 이루어집니다. 많은 트래픽으로 인해 과부하가 발생하는 상황에서 손쉽게 확장 가능한 것은 Pulsar 뿐만 아니라 다른 메시징 플랫폼에서도 활용될 수 있는 점은 사실이지만, 우리가 이야기할 다음 두 가지 이유로 인해 Pulsar와 같은 멀티레이어드 아키텍처에서 훨씬 더 유용합니다.
첫째로, Pulsar의 무상태(stateless) 브로커들은 트래픽이 줄어들면 규모를 축소시킬 수 있는 능력(scale-in)을 제공하며, 이는 퍼블릭 클라우드 환경에서 직접적인 비용 절감으로 이어집니다. 하드 드라이브에 데이터를 포함하고 있기 때문에, 단일 구조의 다른 메시징 시스템들은 노드 규모를 축소할 수 없습니다. 불필요한 노드의 제거는 해당 데이터가 완전히 처리되거나 클러스터에 남아있는 다른 노드로 이동한 후에만 가능하지만 이 두 가지 작업은 쉽게 자동화되어 수행될 수 없습니다
둘째로, Apache Kafka와 같은 모노리식 구조에서 브로커는 장착된 디스크에 저장되어 있는 데이터에 대한 요청만 처리할 수 있습니다. 이는 트래픽 급증에 반응하는 오토 스케일링의 혜택을 제한합니다. 이유는 즉슨, Kafka 클러스터에 새로 추가된 노드들은 제공할 데이터가 없으므로, 기존 데이터를 토픽에서 읽어야하는 요청들을 처리할 수 없으며 쓰기 요청만 처리할 수 있기 때문입니다.
마지막으로, Apache Kafka와 같은 모노리식 구조에서 수평적 확장은 요구되는 리소스의 종류(CPU vs 스토리지) 와 무관하게 컴퓨팅 리소스와 저장 공간 모두 갖춰진 새로운 노드를 추가함으로써 이루어집니다. 따라서 CPU 사용량이 높아짐에 따라 서비스 용량을 확장하면, 실제로 추가 저장 공간이 필요하지 않더라도 저장 용량도 함께 확장되게 됩니다. 반대의 경우도 마찬가지입니다.
AUTO-RECOVERY
프로덕션 환경에 메시징 플랫폼을 도입하기 전에, 단일 노드의 장애부터 다양한 장애 상황으로 부터 복구하는 방법을 알고있어야 합니다. Pulsar와 같은 멀티레이어드 아키텍처에서는 이 과정이 매우 직관적입니다. 브로커 노드가 무상태(stateless)이기 때문에, 실패한 서비스를 대체하기 위해서 서비스 중단이나 데이터 이동과 관련한 고려 사항 없이 새로운 인스턴스를 생성하여 대체할 수 있습니다. 스토리지 계층에서는 데이터의 여러 복제본(replica) 여러 노드에 분산되어 있어, 장애 발생시 새로운 노드로 쉽게 대체할 수 있습니다. 어느 시나리오에서든 Pulsar는 오토스케일링 그룹과 같은 클라우드 제공자 기능을 활용하여 항상 최소한의 노드 수가 실행되도록 보장할 수 있습니다. 또다시 Kafka와 같은 모노리식 구조에서는 새로 추가된 노드들이 제공할 데이터가 없으므로, 쓰기 요청에 대한 처리만 할 수 있게 되는 문제점이 발생합니다.
1.4.2 Message consumption
분산 메시징 시스템에서 메시지를 읽는 방법은 수 많은 동시 컨슈머(concurrent consumer)를 지원하도록 설계되었기, 레거시 메시징 시스템에서 메시지를 읽는 것과는 다소 차이가 있습니다. 데이터가 어떻게 소비되는지는 대부분 시스템 내부에 데이터가 어떻게 저장되는지에 따라 결정되며, 파티션 중심 시스템과 세그먼트 중심 시스템 각자 고유한 방식으로 사용자에게 pub-sub semantic을 지원합니다.
MESSAGE CONSUMPTION IN KAFKA
Kafka에서 모든 컨슈머는 토픽에 대한 단일 논리적 구독자를 형성하는 컨슈머 그룹에 속합니다. 각 그룹은 확장성과 결함 감내(fault tolerance)를 위해 많은 컨슈머 인스턴스로 구성되어, 하나의 인스턴스가 실패하면 나머지 컨슈머 대체됩니다. 기본적(default)으로, 애플리케이션이 Kafka 토픽을 구독할 때마다 새로운 컨슈머 그룹이 생성되고 애플리케이션은 group.id
를 제공함으로써 기존 소비자 그룹을 활용할 수 있습니다.
Kafka 문서에 따르면, "Kafka에서 소비가 구현되는 방식은 로그의 파티션을 컨슈머 인스턴스에 나누어 각 인스턴스가 어느 시점에서든 '공정한 양의' 파티션을 독점적으로 소비하도록 하는 것"(https://docs.confluent.io/5.5.5/kafka/introduction.html)이라고 합니다. 쉽게 말해, 토픽 내의 각 파티션은 한 번에 하나의 컨슈머만 가질 수 있으며, 파티션은 그룹 내의 컨슈머들에게 고르게 분배된다는 것을 의미합니다. Figure 1.17에서 처럼, 컨슈머 그룹의 멤버 수가 파티션 수보다 적으면 일부 컨슈머에 여러 파티션이 할당되지만, 파티션 수보다 컨슈머가 더 많으면 초과 컨슈머는 유휴(idle) 상태를 유지하고 컨슈머 실패 시에만 대체합니다.
독점적인 컨슈머를 생성하는 것의 중요한 부작용 중 하나는 컨슈머 그룹 내에서 활성 컨슈머의 수는 토픽의 파티션 수를 초과할 수 없다는 것입니다. 이러한 제약은 Kafka 토픽에서 데이터 처리량을 확장하는 유일한 방법이 컨슈머 그룹에 더 많은 컨슈머를 추가하는 것으로 한정되기 때문에 문제가 될 수 있습니다. 이 점은 병렬성(parallelism)을 파티션 수로 제한하며, 컨슈머가 토픽 프로듀서를 따라잡지 못하는 경우 데이터 처리량의 확장성을 제한하게 됩니다. 결국 여기서 유일한 해결책은 토픽 파티션 수를 늘리는 것이 되지만, 앞서 이야기한 것처럼 비용, 속도, 복잡도 관점에서 쉽지 않습니다.
Figure 1.17 과 같이, 모든 컨슈머의 메시지는 합쳐져서 Kafka 클라이언트로 전송됩니다. 컨슈머 그룹은 따라서 메시지 순서를 유지하지 않습니다. Kafka는 토픽 내의 파티션이 아닌, 파티션 내의 레코드에 대해서만 순서(total ordering)를 제공합니다.
앞서 언급했듯이, 컨슈머 그룹은 동작 확장성과 결함 내구성을 제공하기위해 클러스터와 같은 역할을 수행합니다. 이 말은 즉 그룹 내의 컨슈머의 추가 또는 손실에 동적으로 반응한다는 것을 의미합니다. 그룹에 새로운 컨슈머가 추가되면, 이전에 다른 컨슈머가 소비하던 파티션에서 메시지를 소비하기 시작합니다. 컨슈머가 종료되거나 충돌할 때도 같은 일이 발생합니다. 그룹에서 제외된 컨슈머가 소비하고 있던 파티션은 남아있는 컨슈머 중 하나에 의해 소비됩니다. 이러한 컨슈머 그룹 내의 파티션 소유권 재분배는 재분배(rebalancing, 리밸런싱)이라고 불리며, 재분배가 실행되기 전에 각 컨슈머의 오프셋이 저장되지 않으면 데이터 손실의 가능성을 포함한 여러 의도치 않은 문제를 야기할 수 있습니다.
여러 애플리케이션이 같은 토픽에서 데이터를 읽어야 하는 경우는 매우 흔한 일이며 그것은 메시징 시스템의 주요 기능 중 하나이기도 합니다. 따라서 토픽은 매우 다른 소비 패턴을 가진 여러 컨슈머 애플리케이션 간에 공유되는 자원이 됩니다. 다음 시나리오를 생각해보겠습니다. 실시간 주가 정보를 "stock quotes"라는 토픽에 스트리밍하고 그 정보를 해당 기업 전체에 공유하려는 금융 서비스 회사가 있습니다. 내부 거래 플랫폼, 알고리즘 거래 시스템, 고객 대면 웹사이트과 같이 비즈니스-핵심(business-critical) 애플리케이션들은 모두 최대한 빨리 토픽 데이터를 처리해야합니다. 이경우 엄격한 SLA를 충족시키는 처리량을 제공하기 위해 많은 양의 파티션 수가 필요함 함을 의미합니다.
1.4.3 Data durability
메시징 시스템의 관점에서 데이터 내구성(data durability)라는 표현은 시스템에 의해 확인(acknowledge**)된 메시지가 시스템 수준 장애가 발생하더라도 살아남음을 보장하는 것을 의미합니다. Pulsar나 Kafka와 같이 수 많은 노드로 구성된 분산 시스템에서는 다양한 장애가 여러 레벨에서 발생할 수 있으므로, 데이터가 어떻게 저장되는지, 그리고 시스템이 제공하는 어떤 내구성 보장들을 제공하는지 이해하는 것이 중요합니다.
프로듀서가 메시지를 발행하면, 메시징 시스템에서 acknowledgement(확인, 줄여서 ack)가 반환되어 토픽에 메시지가 수신되었음을 나타냅니다. 이 ack은 메시지는 안전하게 저장되었으며, 프로듀서는 메시지가 손실될 걱정 없이 프로듀서가 가진 메시지를 폐기할 수 있음을 나타냅니다. 앞으로 살펴볼 내용과 같이 이러한 보장의 정도는 Kafka보다 Pulsar에서 더욱 강력합니다.
DATA DURABILITY IN KAFKA
앞서 언급했듯이, Apache Kafka는 메시지 저장에 대해 파티션 중심(partition-centric)의 저장 방식을 취합니다. 데이터 내구성을 보장하기 위해, 클러스터 내에서 각 파티션의 여러 복제본을 유지하며 일정 수준의 데이터 redundnacy를 제공합니다.
Kafka 는 메시지를 수신할 때, 메시지에 해싱 함수가 적용해서 메시지가 어느 토픽의 파티션에 쓰여야 하는지 결정합니다. 결정한 이후 메시지 내용은 파티션 리더 복제본(leader replica)의 (디스크가 아닌) 페이지 캐시에 쓰여집니다. Figure 1.18에서 보여지는 것과 같이 메시지가 리더에 의해 확인되면, 각 팔로워 replica는 파티션 리더로부터 메시지 내용을 pull 방식으로(즉, 그들은 컨슈머로서 리더로부터 메시지를 읽음) 가져오는 책임이 있습니다. 해당 접근 방식은 최종적 일관성 전략(eventually consistent strategy)라 부르며, 분산 시스템에서 하나의 노드가 가지고 있는 가장 최신 버전의 데이터가 다른 노드들에게 전달되고, 최종적으로 모든 노드가 같은(일관된) 데이터를 가지게 될 때까지 진행됩니다. 이 접근 방식은 수신한 메시지를 저장하는 데 필요한 시간을 줄이는 장점이 있지만, 데이터가 손실될 가능성 두 가지를 가지고 있습니다; 첫째, 리더 노드에서 전원이 나가거나 다른 종류의 프로세스 종료 상황이 발생하는 경우, 페이지 캐시에 쓰여진 데이터 중 로컬 디스크에 영구 저장되지 않은 데이터는 손실됩니다. 두 번째 데이터 손실의 가능성은 기존 리더 프로세스가 실패하고 남아 있는 팔로워 중 하나가 새 리더로 선출되는 경우 발생합니다. 해당 리더 fail-over 시나리오에서 이전 리더에 의해 ack-되었지만 아직 새로 선출된 리더 복제본에 복제되지 않은 메시지는 손실됩니다.
기본적으로, 메시지는 리더가 메모리에 쓴 후에 ack됩니다. 해당 동작은 모든 복제본이 메시지의 사본을 받을 때까지 리더가 ack 하는 것을 보류하도록 설정할 수 있습니다. 이는 팔로워가 네트워크를 통해 정보를 가져와 리더에게 응답을 보내야 하는 기본 복제 메커니즘에는 영향을 미치지 않습니다. 당연히, 이 동작은 성능 저하를 초래하며, 이 부분은 대부분의 발표된 Kafka 성능 벤치마크에서 종종 숨겨져 있습니다. 따라서 해당 설정으로 직접 성능 테스트를 수행하여 예상되는 성능을 더 잘 이해하는 것이 좋습니다.
이 복제 전략의 또 다른 부작용은 오직 리더 replica 만 프로듀서와 컨슈머 모두를 서빙 할 수 있다는 것으로, 가장 최신의 정확한 데이터 사본은 리더만이 확실하게 가지고 있을 것이라는 것이 보장되기 때문에 발생합니다. 모든 팔로워 복제본은 트래픽 급증 시 리더의 부하를 경감할 수 없는 passive 한 노드입니다.
DATA DURABILITY IN PULSAR
Figre 1.19와 같이 Pulsar 는 메시지 수신시에, 메모리에 사본을 저장하고 메시지 퍼블리셔에게 ack를 “보내기 전”에 데이터를 write-ahead log(WAL)에 기록하며 디스크에 기록합니다. 이 방식은 전통적인 데이터베이스의 원자성, 일관성, 고립성, 내구성(ACID) 트랜잭션 의미론을 모델링한 것으로 장애로 인한 실패(failure) 이후 다시 온라인 상태가 되더라도 데이터가 손실되지 않도록 보장합니다.
Pulsar에서는 데이터 복제 요구 사항에 따라 토픽에 필요한 복제본의 수를 설정할 수 있으며, Pulsar는 프로듀서에게 확인(ack)을 보내기 전에 서버의 정족수(quorum)가 데이터를 수신하고 및 확인(ack)하는 것을 보장합니다. 이 설계는 데이터가 기록된 모든 bookie 노드에 동시에 치명적인 장애가 발생하는 매우 희귀한 경우에만 데이터가 손실될 수 있다는 것을 보장합니다. 그러므로 bookie 노드를 여러 지역(region)에 분산시키고, 랙-인지(rack-aware) 노드 배치 정책을 사용하여 데이터의 사본이 하나 이상의 지역이나 데이터 센터에 저장되도록 하는 것을 권장하는 이유입니다.
더 중요한 것은, 이 설계는 데이터가 복제본 간에 동기화되도록 보장하는 2차적인 복제 프로세스의 필요성을 제거하고, 복제 프로세스의 지연으로 인한 데이터 불일치 문제를 해소합니다.
1.4.4 Message acknowledgment
분산 메시징 시스템에서는 실패는 불가피합니다. 분산 메시징 시스템에서는 메시지를 소비하는 컨슈머와 메시지를 서비스하는 메시지 브로커 모두 실패할 수 있습니다. 실패가 발생한 이후 모든 것이 복구되면, 메시지가 건너 뛰어지거나 재처리되지 않도록 컨슈머가 중단한 정확한 지점에서 소비를 재개하는 것이 중요합니다. 이처럼 컨슈머가 소비를 재개해야 하는 지점을 종종 토픽 오프셋(offset)이라고 합니다. Kafka와 Pulsar는 오프셋을 유지하는 데 있어 데이터 내구성에 직접적인 영향을 미치는 서로 다른 접근 방식을 취합니다.
MESSAGE ACKNOWLEDGMENT IN KAFKA
Apache Kafka에서 재개 지점은 컨슈머 오프셋이라고 불리며, 완전히 컨슈머에 의해 제어됩니다. 일반적으로 컨슈머는 메시지 확인을 나타내기 위해 토픽에서 레코드를 읽을 때마다 순차적으로 오프셋을 증가시킵니다. 이 오프셋을 컨슈머의 메모리에만 유지하는 것은 위험합니다. 따라서 이 오프셋들은 __consumer_offsets
라는 별도 토픽에 메시지로 저장됩니다. 각 컨슈머는 주기적인 간격으로 현재 위치를 포함하는 메시지를 해당 토픽에 커밋하는데, Kafka의 자동 커밋 기능을 사용하면 5초마다 발생합니다. 이 전략은 오프셋을 오직 메모리에만 유지하는 것보다는 나은 방법이지만, 이 주기적인 업데이트 접근법에는 몇 가지 문제가 있습니다.
자동 커밋이 5초마다 발생하고 컨슈머가 오프셋 토픽에 가장 최근에 커밋한 후 정확히 3초 후에 종료되는 단일 컨슈머 시나리오를 고려해 보겠습니다. 이 경우, 토픽에서 읽은 오프셋은 3초 전의 것이므로, 그 3초 창에서 도착한 모든 이벤트는 두 번 중복 처리됩니다. 커밋 간격을 더 작은 값으로 설정하며 중복 처리되는 레코드의 범위를 줄일 수는 있지만, 완전히 제거하는 것은 불가능합니다.
Kafka 컨슈머 API는 타이머를 기반으로 한 것이 아니라 현재 오프셋을 커밋할 수 있게 하는 애플리케이션 개발자 관점에서 유의미한 메서드를 제공합니다. 따라서, 메시지 중복 처리를 제대로 제거하기 위해서는, 이 API를 사용하여 성공적으로 메시지 소비할 때마다 오프셋을 커밋할 수 있습니다. 그러나 이 경우에, 정확한 복구 오프셋을 보장하는 부담을 애플리케이션 개발자에게 전가하고, 이제 컨슈머는 각 오프셋을 Kafka 토픽에 커밋하고 ack를 기다려야하는 추가적인 지연 부담을 안게됩니다.
MESSAGE ACKNOWLEDGMENT IN PULSAR
Apache Pulsar는 메시지 acknowledgement를 추적하기 위해 각 구독자에 대한 cursor ledger(커서 원장)이라 불리는 Apache BookKeeper 내부의 원장(ledger)를 관리합니다. 컨슈머가 메시지를 읽고 처리하면, Pulsar 브로커에게 확인(acknowledgment)을 보냅니다. 브로커는 ack를 받는 즉시 해당 컨슈머의 구독(subscription)에 대한 커서 원장을 업데이트합니다. 커서 원장은 BookKeeper의 원장에 저장되므로, 이 정보가 디스크에 fsync되었으며 여러 bookie 노드에 여러 사본이 존재한다는 것을 알 수 있습니다. 커서 원장을 디스크에 유지함으로써, 컨슈머가 나중에 충돌하고 재시작하더라도 다시 메시지를 받지 않게 됩니다.
Apache Pulsar에서는 메시지를 확인하는 두 가지 방법이 있습니다: 선택적(selectively) 또는 누적형식(cumulatively). 누적 확인(cumulative acknowledgment)을 사용하면, 컨슈머는 받은 마지막 메시지만 확인하고. 해당 메시지 ID를 포함하여 토픽 파티션 내의, 해당 메시지 이전에 발생한 메시지들 까지 전부 확인된 것으로 표시되며, 다시 컨슈머에게 재전송되지 않습니다. 누적 확인은 Apache Kafka의 오프셋 업데이트와 사실상 동일합니다.
Apache Pulsar의 Kafka와의 차별점은 선택적 확인으로(selective acknowledgement) 컨슈머가 메시지를 개별적으로 ack(즉, 선택적 확인)할 수 있는 기능입니다. 이 기능은 단일 컨슈머 실패에 대한 메시지 재전송을 가능하게 하므로 토픽 당 다중 컨슈머를 지원하는 데 핵심적인 역할을 수행합니다.
다시 한번 단일 컨슈머 실패 시나리오를 고려해 보겠습니다. 이 시나리오에서 컨슈머는 성공적으로 처리한 후에 메시지를 개별적으로 ack-합니다. 실제 장애로 이어지기 전까지 컨슈머는 부분적으로 메시지 처리에 성공하고 부분적으로 실패했습니다. Figure 1.20은 오직 두 개의 메시지(4와 7)만이 성공적으로 처리되고 확인되었음을 보여주는 예시입니다.
Kafka의 오프셋 개념이 컨슈머 그룹의 오프셋을 모든 메시지가 확인된 것으로 간주되는 지점을 표시하는 high-water mark로 취급하는 것을 고려할 때, 이 시나리오에서 확인된 가장 높은 번호의 메시지 ID가 7이므로 오프셋은 7로 업데이트되었을 것입니다. Kafka 컨슈머는 해당 토픽에서 재시작되면 메시지 8에서 시작하게 되고 계속 진행하게 되어, 메시지 1-3, 5, 6을 건너뛰게 되며, 이들 메시지는 절대 처리되지 않기 때문에 실질적으로 손실됩니다.
Pulsar의 선택적 확인을 사용하는 동일한 시나리오에서는, 컨슈머가 재시작될 때 확인되지 않은 모든 메시지, 즉 메시지 1-3, 5, 6이 모두 재전송되므로, 컨슈머 오프셋 제한으로 인한 메시지 손실을 피할 수 있습니다.
1.4.5 Message retention
ActiveMQ와 같은 레거시 메시징 시스템과 달리, 분산 메시징 시스템에서는 모든 컨슈머에 의해 ack된 메시지가 즉시 제거되지 않습니다. 레거시 시스템들은 제한된 리소스를 가지고 있기 때문에 가능한 많은 로컬 디스크 공간을 확보하기 위한 방법으로 이러한 접근법을 취했습니다. Kafka와 Pulsar와 같은 분산 메시징 시스템은 수평적 확장이 가능한 메시지 저장을 통해 이 제약을 어느 정도 완화했으며, 이 두 시스템 모두 디스크 공간을 회수하는 메커니즘을 제공합니다. 자동 메시지 삭제는 제대로 설정되지 않으면 실수로 데이터 손실을 초래할 수 있기 때문에 이 두 시스템에서 자동 메시지 삭제가 어떻게 처리되는지 정확히 이해하는 것은 중요합니다.
MESSAGE RETENTION IN KAFKA
Kafka는 토픽에 발행된 모든 메시지를 설정한 보존 기간 동안 보관합니다. 예를 들어, 보존 정책이 7일로 설정된 경우, 메시지가 토픽에 게시된 직후 7일 동안 메시지 소비가 가능합니다. 보존 기간 경과 후에는 메시지 공간을 확보하기 위해 폐기됩니다. 해당 삭제 동작은 메시지가 소비되고 ack-되었는지 여부와 관계없이 발생합니다. 당연하게도, 보존 기간이 모든 컨슈머가 메시지를 소비하는 데 걸리는 시간보다 짧은 경우, 예를 들어 컨슈머 시스템의 장기간 다운되는 경우, 데이터 손실 가능성이 발생합니다. 이러한 시간 기반 접근 방식의 또 다른 단점은 모든 관련 컨슈머에 의해 소비된 후에도 메시지를 불필요하게 오래 보존할 확률이 높다는 것입니다. 이는 저장 용량의 비효율적인 방식입니다.
MESSAGE RETENTION IN PULSAR
Pulsar에서 컨슈머가 메시지를 성공적으로 처리하면, 브로커가 메시지를 폐기할 수 있도록 브로커에게 확인(acknowledgment)을 보내야 합니다. 기본적으로 Pulsar는 모든 컨슈머에 의해 확인된 모든 메시지를 즉시 삭제하고, 확인되지 않은 모든 메시지를 메시지 백로그에 보관합니다. Pulsar에서는 모든 구독자들이 이미 소비한 후에만 메시지를 삭제할 수 있습니다. 또한 Pulsar는 또한 메시지 보존 기간을 설정하여 모든 구독자들이 이미 소비한 후에도 메시지를 더 오래 보관할 수 있는 기능을 제공합니다. 이에 대해서는 2장에서 더 자세히 논의하겠습니다.