티스토리 뷰
참고한 글
보니까 저자가 rabbitmq 코어팀에서 일하는 사람인 것 같다.
그래서 카프카보다 rabbitmq 장점을 강조해서 말하는 것 같긴 한데, 글 퀄리티가 좋았다.
Event-Driven Architectures - The Queue vs The Log
Event-Driven Architectures - Queue vs Log - A case study
다른 읽어보면 좋은 글
RabbitMQ vs Kafka Part1 - Two Different Takes on Messaging
😎
Queue based => RabbitMQ, ActiveMQ, MSMQ, AWS SQS, JMQ and many more.
Log based => Apache Kakfa, Apache Pulsar, AWS Kinesis, Azure Event Hubs and many more.
*(참고) 지원하는 프로토콜
rabbitmq
AMQP 0-9-1 (default), STOMP, MQTT, AMQP 1.0, HTTP and Websocket ( 나머지는 플러그인을 통해야 함)
kafka
a binary protocol over TCP (자체 프로토콜)
(들어가기 전에..)
연관된 이벤트끼리 같은 큐/로그(카프카로 치면 topic)에 들어가야 하는 이유
=> 시간 순으로 처리됨을 보장하기 위해서!
예를 들어 "주문"과 "주문 취소" 이벤트의 경우 항상 [주문 > 주문 취소] 순으로 처리 되어야하지만, 각각 다른 큐/로그에서 이벤트를 읽는다고 하면 "주문"후 바로 "주문 취소" event publish 같은 상황에서 처리 순서를 보장하지 못할 가능성이 농후하다.
큐
"transient(일시적) in nature" => consumer가 메세지를 읽으면 pop 돼서 사라진다.
그렇기 때문에 각 consumer는 각자의 큐를 가지고 있다. => 다른 application끼리 같은 큐를 공유하면 메세지 consume에 경쟁 상황이 발생하므로 각자 자신의 큐가 필요하다.
라우팅 (각 application 큐로 메세지 친히 전달) , push 모델 => 동일한 메세지를 여러 application에 전달하고 싶다면 pub/sub 패턴을 도입하면 되는데, publisher는 topic (rabbitmq로 친다면 exchange)에 메세지를 발행하고 topic을 subscribe한 (rabbitmq에서는 binding이라고 함) consumer의 큐에 배달해준다. 위의 블로그에서는 exchange는 physical한 객체라기 보다는 추상적인 라우팅 규칙 같은 것으로 받아들이면 된다고 설명한다.
사실 rabbitmq 구조는 거의 이게 다라서 카프카 보다는 엄청 간단한 것 같다.
한 consumer가 소비하는 임의의 event 사이의 global 한 시간순 order 보장 =>
consumer입장에서 자기가 소비하는 이벤트들은 (어떤 토픽에서 라우팅 돼서 왔든지 간에) 일단 한 개의 큐(자기 큐)에 모였다가 FIFO로 처리되기 때문
중간 수정이 쉬운편 => abstract concept 인 topic (rabbitmq의 exchange, 라우팅 규칙같은 역할!) 을 도입하면서 publish 와 consuming이 decouple 된다.
로그
"persistent in nature" => 정해둔 retention policy 기간동안 사라지지 않고 로그처럼 쌓인다.
temporal constraint으로부터 자유롭다 => consumer가 메세지를 한번만 consume할 수 있는 제약으로부터 자유로움. 과거로 거슬러 올라가서 읽을 수도 있음. (이벤트 소싱 가능)
여러 consumer가 (consumer 그룹이) 같은 로그를 share 한다. => 여러 consumer가 같은 로그의 다른 offset을 동시에 읽는 것이 가능하다. (consumer 그룹 별로 어느 offset까지 읽었는지 기록)
라우팅 같은 건 없음. pull 모델 => consumer(application)는 관심있어서 subscribe한 여러개의 topic에서 업데이트 된 메세지를 직접 읽어들여야 함.
이것과 관련해서, 토픽을 크게 하면 이 중 가끔 일어나는 몇개의 이벤트에만 관심있는 consumer도 다른 이벤트 발행량이 많을 때 쓸데없는 데이터 읽느라 스케일 아웃 해야하는 side effect가 발생할 수 있다.
예를 들어 이렇게 모든 이벤트들이 모두 orders 토픽으로 발행되도록 한다면
빌링은 자신이 발행한 이벤트도 읽어들여야하고
배송 이벤트가 dominate하게 high volume으로 발생하고 있을 경우, 빌링은 읽더라도 필요 없어서 무시하는 메세지인데도 같이 scale out 해야 한다.
한 consumer가 소비하는 이벤트 중 같은 토픽에서 읽어들인 이벤트만 시간 순서 보장
미리 아키텍처적인 관점에서 topic 구성에 대한 고민 필요 => 어떤 이벤트들을 같은 토픽으로 묶을지 미리 잘 고민해서 설계해야 한다. 서비스 중간에 수정하기 어렵다. log라서 토픽 수정전 이벤트도 긴 시간 남아있을 거고, consumer가 어떤 topic subscribe할지도 수정해 줘야한다.
scaling issue
scaling, 대규모, 분산작업을 support하는데 있어서는 kafka가 훨씬 잘 하는 것 같다.
topic을 여러 파티션으로 쪼개서 브로커(브로커가 한 서버내에 여러개 있거나, 클라우드의 인스턴스 같은 것으로 떠있을 수도 있지만 어쨌든 각자 디스크를 가진 물리적인 컴퓨팅 단위 같은 것) 에 나눠놓는 다거나, 같은 컨슈머 인스턴스들을 묶어서 컨슈머 그룹으로 부르고 하는 등 애초에 기본 아키텍처에서부터 분산, scaling에 대한 고려를 반영한 것만 봐도.
Kafka also manages our consumers and performs automatic partition assignment for our consumers. This is where RabbitMQ isn't so great.
With RabbitMQ we must use the Consistent Hash Exchange and
manually implement the consumer group logic ourselves by using Single Active Consumer and custom hand-rolled logic.
참고하기
understanding kafka partition assignment strategies and how to write your own custom assignor
kafka 운영자가 말하는 kafka consumer group
rabbitmq의 solution
하나의 큐를 parallel하게 consume하도록 scale out하면 큐 consume에 경쟁상황이 발생한다! (= Processing a single sequence in parallel means you lose the ordering! )
rabbitmq는 log based system이 파티션을 나누듯이 Consistent Hashing Exchange를 도입해서 해결.
=> 라우팅키나 메세지 헤더로 hash based 로드 밸런싱을 한다. 같은 라우팅 키(혹은 메세지 헤더)를 가진 메세지는 항상 동일한 큐로만 배달되도록 한다.
Consistent Hashing
'시리즈 > Concurrency' 카테고리의 다른 글
IPC, 프로세스간 통신 그리고 gRPC (0) | 2021.12.05 |
---|---|
서버는 여러개의 request를 어떻게 감당할까? (0) | 2021.11.29 |
RabbitMQ 왕기초 (0) | 2021.10.28 |
동시성 재료 살펴보기 (0) | 2021.10.27 |
Javascript 반복문 비동기 처리 ( + Event Loop, Promise ) (0) | 2021.03.23 |