어제 테크 미팅 때, RabbitMQ와 Kafka에 대한 이야기가 나왔다.
본 글에서는 두 가지 기술을 더 광범위한 관점에서 생각해보고, 두 시스템이 제공하는 기능에 초점을 맞췄다. 이를 통해 어떤 시스템을 언제 사용하는게 좋은지 결정을 내리는데 도움이 되었으면 좋겠다.
웹 검색을 했을 때, 일부 글은 RabbitMQ보다 Kafka가 더 뛰어나다고 주장하고, 다른 글에서는 그 반대인 경우가 많다. 그렇기 때문에 서로 혼선이 오는 것 같다.
RabbitMQ와 Kafka 중 무엇을 선택할지에 대한 결정은 프로젝트의 요구 사항에 따라 달라지며, 적절한 시나리오에서 둘 다 사용해 본 경우에만 비교가 가능하다는 것을 아는 것이 중요하다.
두 시스템 모두 큐 또는 토픽을 통해 생산자와 소비자 간 메시지를 전달한다. 메시지에는 모든 종류의 정보가 포함될 수 있다. 예를 들어서 웹사이트에서 발생한 이벤트 혹은 다른 애플리케이션에서 이벤트를 트리거하는 메시지가 될 수 있다.
RabbitMQ와 Kafka 같은 시스템은 서로 다른 구성 요소를 연결하고, 데이터를 전달하는 용도로 사용될 때 이상적이다.
비교해보기
RabbitMQ는 AMQP, MQTT, STMOP 등과 같은 다양한 프로토콜을 지원하는 메시지 브로커이다. 높은 처리량을 처리할 수 있다.
Kafka는 높은 유입 데이터 스트림과 메시지 재생에 최적화된 메시지 버스이다. 애플리케이션이 디스크에서 스트리밍된 데이터를 처리하고 다시 처리할 수 있는 내구성 있는 메시지 브로커로 볼 수 있다.
RabbitMQ는 2007년에 출시되었고, Kafka는 2011년에 출시되었기에, 2024년이 지금 시점에서는 둘 다 신뢰할 수 있는 성숙한 시스템으로 인지할 수 있다.
그리고 세월이 흘러서 RabbitMQ도 Stream을 지원하기 시작했다. 브로커로서 RabbitMQ의 메시지는 소비되고 확인되면 큐에서 삭제된다. 이런 특성으로 인해 데이터를 장기로 보관해야 하는 지속성이나 메시지를 다시 재생해야 하는 시나리오에는 적합하지 않았다. 그러나 RabbitMQ v3.9에서는 모든 것이 바뀌었다.
Stream Queues는 RabbitMQ v3.9에 도입되었다. 지속적이고 복제되며, 기존 큐와 마찬가지로 소비자가 읽을 수 있도록 메시지를 버퍼링한다. 그러나 Streams는 아래 측면이 기존 큐와 다르다.
- 생산자가 메시지를 쓰는 방법
- 소비자들이 메시지를 읽는 방법
Stream에 쓰여진 메시지는 지울 수 없고, 읽을 수만 있다. RabbitMQ에서 Stream의 메시지를 읽으려면 하나 이상의 소비자가 구독해야 하고 원하는 횟수만큼 동일한 메시지를 읽는다.
위 기능은 RabbitMQ의 스트림 큐를 Kafka와 유사한 기능을 찾는 사용자에게 매력적인 옵션으로 만든다.
RabbitMQ의 Streams에 대한 설명을 여기까지 하고 본론으로 돌아가서 두 가지 기술을 바라볼때, 가장 중요한 점은 “언제 Kafka를 사용하고, 언제 RabbitMQ를 사용해야 하는가?” 이다.
메시지 처리 (메시지 재생)
기존 RabbitMQ 큐는 소비자가 메시지를 수신할 때까지만 메시지를 저장했지만, Streams는 이를 대폭적으로 변경했다.
메시지 재생에 관해 RabbitMQ와 Kafka는 모두 같은 방식으로 처리한다. Kafka와 RabbitMQ Streams에 전송된 데이터는 지정된 보관 기간(기간 또는 크기 제한)이 지날 때까지 저장된다. 메시지는 보관 기간/크기 제한을 초과할 때까지 큐에 남아 있으므로 소비된 메시지는 제거되지 않는다. 대신 여러 번 재생되어 소비될 수 있다.
RabbitMQ와 Kafka에서 제공하는 리플레이에 대한 기능은 소비자에게 버그가 있어서 일부 또는 모든 메시지를 다시 처리해야 하는 경우다. 이 경우외에 이벤트를 여러 번 재생하는 것은 바람직하지 않다.
규약
RabbitMQ는 AMQP, MQTT, STOMP 등과 같은 여러 표준화된 프로토콜을 지원한다.
Kafka는 애플리케이션과 클러스터 간 통신을 위해 TCP/IP 기반 자체 프로토콜을 사용한다. 이 프로토콜은 간단히 제거하여 교체할 수 없다. 이것만 쓰기 때문이다.
따라서, RabbitMQ가 다양한 프로토콜을 지원하기에 다양한 시나리오에서 사용할 수 있다.
라우팅
RabbitMQ의 가장 큰 이점은 메시지를 유연하게 라우팅 할 수 있다는 것이다. 다이렉트 또는 정규 표현식 기반 라우팅을 사용하면 추가 코드 없이도 메시지가 특정 대기열에 도달할 수 있다.
RabbitMQ는 다이렉트, 토픽, 팬아웃 및 헤더 교환의 라우팅 옵션이 있다.
Kafka는 라우팅을 지원하지 않는다. Kafka 토픽은 변경 불가능한 순서로 메시지를 포함하는 파티션으로 나뉜다. RabbitMQ의 라우팅을 대체하기 위해 소비자 그룹과 영구 토픽을 사용할 수 있다. 모든 메시지를 해당 토픽으로 보내지만 소비자 그룹이나 다른 오프셋에서 구독하도록 할 수 있다.
오프셋은 각 메시지에 부여된 고유 ID로, 메시지 로그에서 메시지의 순서를 나타낸다.
Kafka 스트림을 사용하면 이벤트를 토픽에 동적으로 라우팅하는 것을 만들 수 있지만, 이 기능은 기본 기능이 아니다.
메시지 우선 순위
RabbitMQ는 우선순위 대기열이라는 것을 지원하는데, 이는 대기열에 다양한 우선순위로 설정할 수 있다는 것을 의미한다. 각 메시지의 우선순위는 게시될 때 설정할 수 있다.
우선순위 대기열은 언제 사용할 수 있을까? 예를 들면, 어떤 데이터를 매일 백업을 하는데 수천 개의 백업 이벤트가 순서 없이 추가되는 상황에서, 새 백업 이벤트를 우선순위를 높여서 먼저 처리하게 하고 싶을때 사용할 수 있다.
Kafka는 메시지를 우선순위 수준으로 보내거나 우선순위 순서대로 전달할 수 없다. Kafka의 모든 메시지는 수신 순서대로 저장되고 전달된다.
확인(Commit or Confirm)
확인이라는 단어의 의미는 전송되거나 메시지가 수신되었음을 나타내기 위한 신호이다.
Kafka와 RabbitMQ는 게시된 메시지가 브로커에 안전하게 도달했는지 확인하기 위해 생산자 확인 기능을 지원한다.
노드가 소비자에게 메시지를 전달할 때, 소비자가 메시지를 수신한 것으로 간주해야 하는지 결정해야 한다.
RabbitMQ는 메시지가 전송되면 전달된 것으로 간주하거나, 메시지를 수신한 후 소비자가 수동으로 확인할 때까지 기다릴 수 있다.
Kafka는 파티션의 각 메시지에 대한 오프셋을 유지한다. 커밋된 위치는 저장된 마지막 오프셋이다. Kafka를 사용하는 소비자는 오프셋을 주기적으로 자동 커밋하거나 커밋된 위치를 수동으로 제어하도록 선택할 수 있다. (Kafka에서 메시지 소비에 대해 추적하는 방식은 버전마다 다르다.)
RabbitMQ는 메시지를 처리하지 못할 때 메시지를 nack 할 수 있다. 메시지는 마치 새 메시지인 것처럼 원래 있던 큐로 반환된다. 이는 소비자 측에서 일시적인 오류가 발생한 경우에 유용하다.
대기열 처리
RabbitMQ의 기존 큐는 비어 있을 때 가장 빠르다. 그러나 RabbitMQ Streams와 Kafka는 모두 대량의 메시지를 보관하고 배포하도록 설계되었다.
RabbitMQ의 기존 큐는 “지연 모드”를 사용할 수 있다. 지연 큐는 메시지가 자동으로 디스크에 저장되어 메모리 사용량을 최소화하지만 처리량의 시간을 늘리는 큐이다. 지연 큐는 더 나은 예측 성능을 가진 안정적인 클러스터를 만든다. 한 번에 많은 메시지를 보내는 경우 혹은 소비자가 게시자의 속도를 지속적으로 따라가지 못할 것이라고 생각되는 경우 지연 큐를 활성화 하는 것이 좋다.
스케일링
스케일링은 시스템의 용량을 늘리거나 줄이는 과정을 의미한다. RabbitMQ와 Kafka는 다양한 방식으로 스케일링을 할 수 있으며, 소비자 수, 브로커의 파워를 조정하거나 시스템에 노드를 추가할 수 있다.
소비자 확장
RabbitMQ에서 소비할 수 있는 것보다 더 빨리 메시지가 게시되면 대기열이 커지게 되고, 많은 메시지가 생겨 결국 RabbitMQ의 메모리가 소진될 수 있다. 이 경우 메시지를 처리(소비)하는 소비자의 수를 확장할 수 있다. RAbbitMQ의 각 대기열에는 많은 소비자가 있을 수 있으며 이러한 소비자는 모두 대기열의 메시지를 소비하기 위해 “경쟁” 할 수 있다. 메시지 처리가 모든 활성 소비자에게 분산되기에 RabbitMQ에서 확장 또는 축소는 소비자를 추가하고 제거하기만 하면 된다.
Kafka에서 소비자를 분산하는 방법은 토픽 파티션을 사용하는 것이다. 이렇게 되면 그룹의 각 소비자는 하나 이상의 파티션을 사용한다. 파티션 매커니즘을 사용하여 각 파티션에 비즈니스 키(사용자ID 등)에 따라 다른 메시지 세트를 보낼 수 있다.
브로커 확장
RabbitMQ는 수평 확장이 항상 더 나은 성능을 제공하지 않는다. 가장 좋은 성능 수준은 수직 확장으로 달성된다. 수평 확장도 가능하지만, 노드 간에 클러스터링을 설정해야 하기에 설정 속도가 느려질 수 있다.
Kafka는 클러스터에 노드를 더 추가하거나 토픽에 파티션을 더 추가하여 확장할 수 있다. 이는 RabbitMQ가 해야 하는 것처럼 기존 머신의 CPU나 메모리를 추가하는 것보다 더 쉬울 수 있다.
로그 압축
RabbitMQ에는 없지만 Kafka에는 있는 기능이 로그 압축 전략이다. 로그 압축은 Kafka가 단일 토픽 파티션의 큐내에서 각 메시지 키에 대해 항상 마지막인 값을 유지하도록 보장한다. Kafka는 단순하게 메시지의 최신 버전을 유지하고 동일한 키를 가진 이전 버전을 삭제한다.
로그 압축은 Kafka를 데이터베이스로 사용하는 방법으로 볼 수 있다. 보존 기간을 “영구”로 설정하거나 토픽에서 로그 압축을 활성화하면 데이터가 영원히 저장된다.
압축된 로그는 충돌이나 장애 후 시스템을 복원해야 할 때 유용하다.
모니터링
RabbitMQ는 웹 브라우저에서 서버를 모니터링할 수 있다. 대기열, 연결, 채널, 교환, 사용자 및 사용자 권한을 브라우저에서 처리할 수 있으며 메시지 속도도 모니터링하고 메시지를 수동으로 보내고 받을 수 있다.
Kafka는 모니터링을 위해 오픈소스 툴을 사용해야 한다.
Push와 Pull
RabbitMQ에서는 메시지를 소비자에게 푸시한다. 소비자가 받을 수 있는 여력을 보장하기 위해 사전에 제한을 구성하는 것이 중요하다. (소비자가 처리하는 것보다 메시지가 빠르게 갈 경우) 소비자는 RabbitMQ에서 메시지를 풀링할 수 있지만 권장하지 않는다.
Kafka는 풀 모델을 사용한다. 소비자는 주어진 오프셋에서 메시지를 요청한다. (소비자가 처리하는 것보다 메시지가 빠르게 갈 수 없는 장점)
라이센스
RabbitMQ는 2023년에 브로드컴의 일부가 되었다. 그러나 소스 코드는 모질라 퍼블릭 라이센스로 공개되어 있다. 라이센스는 변경된 적이 없다.
Kafka는 링크드인에서 만들었다. 2011년에 Apache 재단으로 넘어갔다. 따라서 Kafka는 Apache 2.0 라이센스의 적용을 받는다.
두 라이센스 모두 무료 오픈 소스 라이센스이다.
복잡성
개인적인이긴 하지만 RabbitMQ가 더 쉽다. 그 이유는 Kafka의 경우는 토픽/파티션/메시지 오프셋 등과 같은 더 많은 개념을 이해해야 한다. 그래서 더 많은 복잡성이 있다.
Kafka 생태계
Kafka는 단순한 브로커가 아니라 스트리밍 플랫폼이다. 그래서 Kafka와 쉽게 통합할 수 있는 많은 도구가 존재한다. 예를 들어 Kafka Core, Kafka Streams, Kafka Connect, Kafka REST Proxy 등이다. 그러나 대부분 도구는 Confluent에서 제공되며 Apache의 일부가 아니다.
도구가 많다는 점은 연결되는 시스템을 구성할 수 있다는 장점이 있다.
Kafka REST Proxy는 클러스터에서 메타 데이터를 수신하고 간단한 REST API를 통해 메시지를 생성하고 소비할 수 있도록 제공한다.
Kafka Connect는 다른 시스템을 Kafka와 통합할 수 있다.
일반적인 사용 사례
지금까지는 각 시스템이 무엇을 할 수 있고, 못하는지에 대한 얘기를 했다. 이제는 어떤 시스템을 사용할지 생각하고 결정을 내리는 방법에 대해서 설명하려고 한다.
RabbitMQ의 사용 사례
일반적으로 단순하고 전통적인 Pub-Sub 메시지 브로커를 원한다면 RabbitMQ가 확실한 선택이다. 채널 및 큐를 통해 통신을 처리하고 스트리밍을 옵션으로 추가하는 것이 요구 사항이라면 RabbitMQ를 선택했을 것이고 초보자에 더 친화적인 옵션이라고 생각할 수 있다. 그리고 Streams 지원이 있다.
복잡한 라우팅
RabbitMQ는 복잡한 라우팅에서 빛을 발한다. 다른 메시징 시스템과 달리 메시지 라우팅 프로세스를 완벽하게 제어할 수 있다. 콘텐츠 유형, 메시지 우선 순위 및 비즈니스 로직과 같은 여러 조건에 따라 메시지를 라우팅하도록 시스템을 구성할 수 있다.
우선 순위 대기
RabbitMQ는 특정 메시지를 다른 메시지보다 우선시할 수 있는 기능인 우선 순위 큐를 지원한다. 우선 순위 큐를 사용하면 시스템에 부하가 많을 때에도 중요한 메시지가 먼저 처리되도록 할 수 있다.
다중 프로토콜
RabbitMQ는 여러 프로토콜을 지원하기 위해 기능을 확장하고 있다. 현재 AMQP, MQTT, STOMP를 지원한다. 그러나 RabbitMQ의 아키텍처는 AMQP를 위해 설계되었기에 다른 프로토콜을 효율적으로 실행하는데에는 어려움을 겪을 수 있다.
Kafka의 사용 사례
일반적으로 스트리밍 데이터를 저장, 읽기(다시읽기), 분석하기 위해 순수하게 설계된 것을 원한다면 Kafka를 사용해야 한다. 감사를 받는 시스템이나 메시지를 영구적으로 저장해야 하는 시스템에 이상적이다.
즉, 로그 처리, 스트림 처리, 분산 시스템에 특히 유용하다.
높은 처리량
Kafka의 가장 주목할 만한 특징은 대량의 데이터를 처리할 수 있는 능력이다. 초당 수십만 개의 메시지를 처리하도록 설계되어 실시간으로 대량의 데이터를 처리해야 하는 애플리케이션에 탁월한 선택이다.
로그 처리 및 스트림 처리
Kafka는 로그 처리 및 스트림 처리에도 적합하다. Kafka는 내장된 로그 저장 시스템을 통해 다양한 소스의 로그 데이터를 효율적으로 저장하고 처리할 수 있다. 또한 스트림 처리를 지원하여 데이터가 도착하는대로 실시간으로 처리할 수 있다.
분산 시스템
Kafka는 분산 시스템에 탁월하다. 분산 아키텍처를 통해 수평적으로 확장하여 증가된 데이터 부하를 수용할 수 있다. 또한 일부 서버가 실패해도 데이터가 안전하게 보호되도록 한다.
어떤 상황에서 어떤 시스템을 쓸 것인지, 현명하게 결정해야 한다.