Asynchronous Messaging Pattern
•
비동기 메세징은 확장성과 안정성, 성능 향상을 목적으로 발행자(producer)의 메세지 생성이 구독자(consumer)의 작업과 분리되는 방식이다.
•
메세징 시스템을 다룰 때, 일반적으로 Message Queue, Event Stream, Publish/Subscribe 3가지로 나뉜다.
•
대표적인 Message Queue 서비스
◦
RabbitMQ
◦
AWS SQS
◦
Apache Pulsar
•
대표적인 Event Stream 서비스
◦
Apache Kafka
◦
AWS Kinesis
◦
Redis Stream
◦
Apache Pulsar
Message Queue 특징
•
점대점(Point-to-Point) 통신
◦
일반적으로 하나의 메시지는 하나의 소비자에게만 전달된다.
•
메시지 저장
◦
메시지는 소비자에게 전달될 때까지 안전하게 큐에 저장된다.
•
순차적 처리
◦
메시지는 보통 FIFO (First-In-First-Out) 순서로 처리된다.
•
확실한 전달
◦
메시지는 성공적으로 처리될 때까지 큐에 보관되며, 처리에 실패하면 다시 큐에 반환될 수 있다.
•
비동기 통신
◦
메시지 큐는 시스템 간 또는 시스템 내의 비동기 통신을 지원한다.
•
분산 처리
◦
여러 소비자가 동일한 큐에서 동시에 메시지를 처리하여 작업 분산을 지원한다.
•
Message Queue는 메세지 처리 작업을 Consumer에게 위임한다. 따라서, 작업이 한 번만 실행되도록 할 수 있다.
•
Message Queue는 MSA에서 많이 사용된다. 클라우드 기반 또는 서버리스 애플리케이션을 개발할 때 부하에 따라 앱을 scaling 할 수 있다.
◦
예를 들어, queue에 처리되어야 할 메세지들이 많이 있다면, 같은 메세지 큐를 listen하는 여러개의 Consumer들을 추가(scale out) 할 수 있다.
◦
메세지가 빨리 처리된다면, 트래픽이 적을 때는 비용을 절약하기 위해서 Consumer를 종료 할 수도 있다.
•
RabbitMQ와 같은 전통적인 Message Queue(broker)의 형태는 특정 상황에서 소비자와 메시지 브로커의 결합력이 높아지게 되어 후에 트래픽이 증가하여도 수평적으로 확장하는 데에 어려움이 있다.
◦
RabbitMQ에서는 메시지의 라우팅을 결정하기 위해 exchanges와 queue를 사용하는데, 이 설정들은 종종 코드 내에 구성되어 있으며, 변경이 필요할 때마다 소비자의 코드를 수정해야 할 수도 있다.
◦
queue는 특정 노드에 위치하므로, queue 레벨에서의 확장이나 리밸런싱은 추가적인 구성이나 관리를 필요로 할 수 있다. 이것은 큰 트래픽 증가 시 동적으로 수평 확장하는 것을 어렵게 만들 수 있다.
•
이벤트 메시지가 성공적으로 전달되었다고 판단될 경우 이 메시지가 큐에서 삭제되어버리기 때문에 후에 다시 이벤트를 재생하기가 어렵다는 단점이 있다.
Message Queue vs Message Broker 차이는?
Message queue는 queue를 사용해 데이터의 전송, 수신 저장하는 어플리케이션들의 통신에 책임을 가집니다. message queue를 통해 데이터가 전달되지만, 이 데이터들의 내용을 전혀 알지 못합니다.
Message broker는 message queue의 사용을 상속하는 메커니즘으로, message queue와는 달리 관리되는 데이터들의 정보를 알고 있습니다.
즉, message queue는 발행되고 소비되는 데이터를 저장하는 구조이고, message borker는 message queue를 관리하는 소프트웨어 컴포넌트에 가깝습니다.
요약
•
기본 개념: 메시지 큐는 메시지를 순차적으로 저장하고, 송신자(sender)와 수신자(receiver) 사이에서 메시지를 전달하는데 사용됩니다. 메시지는 큐에 도착한 순서대로 처리된다.
•
사용 사례: 비동기 통신, 시스템 간 결합도 감소, 부하 분산, 장애 복구 등에 유용하다.
•
특징: 일반적으로 Point-to-Point 통신 모델을 따릅니다. 한 메시지는 정확히 하나의 소비자에 의해 처리된다.
Event Stream 특징
•
시간 순서 데이터 처리
◦
이벤트나 메시지는 시간 순서에 따라 스트림에 저장되고 처리된다. 이를 통해 이벤트의 순차적인 관계를 유지할 수 있다.
•
다대다 통신 가능
◦
하나의 스트림은 여러 발행자로부터 이벤트를 받을 수 있으며, 여러 소비자가 동시에 데이터를 소비할 수 있다.
•
독립적인 소비자 진행
◦
각 소비자는 자신의 속도로 스트림을 소비할 수 있으며, 동일한 스트림에서 서로 다른 위치의 데이터를 읽을 수 있다.
•
데이터 복제 및 재처리 가능
◦
소비자가 이벤트를 처리한 후에도 이벤트는 스트림에 남아 있어, 필요 시 데이터를 다시 읽거나 이전 상태로 롤백할 수 있다.
•
확장성과 내결함성
◦
스트림 처리 시스템은 종종 분산 아키텍처를 사용하여 확장성과 내결함성을 제공한다.
•
실시간 데이터 처리
◦
스트림은 실시간 또는 거의 실시간 데이터 처리를 지원하여, 신속한 인사이트 및 의사 결정을 가능하게 한다.
•
복잡한 이벤트 처리(Capability)
◦
스트림 처리 시스템은 종종 복잡한 이벤트 처리 기능을 제공하여, 시간에 따른 패턴, 이벤트 상관 관계 등을 분석할 수 있다.
•
전통적인 Message Queue의 단점을 극복하며 생긴 것이 바로 Event-Streaming이다.
•
Message Queue와 Event-Streaming 모두 이벤트를 수신하고, 이것을 소비자에게 전달하는 데에 목적을 두고 있지만 작동 방식에 큰 차이가 있다.
•
Event-Streaming은 Message Queue와 다르게 Topic이라는 것이 Event streamer에 저장된다.
•
Producer가 이벤트를 생성하면, 토픽이라고 불리는 이벤트의 레코드 로그를 streamer에 순서대로 기록하게 된다. 그 후 해당 토픽을 구독(subscribe)한 Consumer에게 전달하게 된다.
◦
또한 이 토픽을 Consumer가 가져간 후에도 이벤트 스트림에서 계속 토픽을 유지하기 때문에 오류 수정이 필요하거나 앱을 리빌드 하는 등의 상황에서 이벤트를 다시 재생시킬 수 있다.
•
이러한 특징으로 인해 Event-Streaming은 전통적인 Message Queue에 비해 좀 더 유연하고 느슨한 결합을 가지고 있고, 따라서 격리와 확장이 비교적 더 쉽다는 장점이 있다.
요약
•
기본 개념: 스트림은 연속적인 데이터 플로우를 나타내며, 메시지(또는 이벤트) 스트림을 통해 실시간 데이터 처리를 가능하게 합니다.
•
사용 사례: 실시간 데이터 처리, 이벤트 소싱, 로그 처리, 스트리밍 애널리틱스 등에 적합합니다.
•
특징: 데이터는 시간 순서에 따라 저장되며, 여러 소비자가 동일한 스트림을 독립적으로, 혹은 서로 다른 속도로 소비할 수 있습니다. 이는 데이터의 이력을 유지하며, 소비자가 필요에 따라 데이터 스트림의 어느 지점에서든 읽기 시작할 수 있음을 의미합니다.
RabbitMQ vs Kafka
•
RabbitMQ의 경우 kafka에 비해 좀 더 쉽지만 컨슈머와 메시지 브로커간의 결합도가 높기 때문에 트래픽이 작으면서 비즈니스가 후에 확장되지 않을 확률이 높다면 RabbitMQ를 사용하는 것이 좋다.
•
하지만 대규모 트래픽이 예상되고, 추후 확장이 예상된다면 kafka를 선택하는 것이 좀 더 바람직해보인다.
Pub/Sub 특징
•
비동기 통신
◦
발행자는 메시지를 게시하고 구독자에게 즉시 전달될 것을 기대하지 않는다.
•
다대다 통신
◦
하나의 메시지는 여러 구독자에게 전달될 수 있다.
•
낮은 결합도
◦
발행자는 구독자의 존재나 구독자에게 어떤 메시지가 전달되었는지를 알 필요가 없다.
•
주제 기반 필터링
◦
메시지는 주제나 채널을 기반으로 분류되며, 구독자는 특정 주제에 대해서만 메시지를 받는다.
•
동적 구독
◦
구독자는 언제든지 주제에 구독하거나 구독 취소할 수 있다.
•
Pub/Sub에서, 모든 구독자(subscriber)는 발행자(publisher)가 exchange에 전송하는 메세지 복사본을 1개 이상 가진다.
•
Pub-Sub은 모든 구독자(subscriber)들이 메세지의 복사본을 가지도록 보증할 때 사용한다.
이때 구체적인 발행/구독 방식이 각 서비스마다 다른데, 대표적으로 Kafka와 Redis가 있다.
Kafka의 Pub/Sub
•
Kafka의 topic은 우체통이라고 생각하면 될 것이다.
•
철수(Producer)는 편지를 써서 영희네 우체통(Topic)에 넣었다. 이때, 편지는 누군가에 의해 발견될 때까지 우체통에 얌전히 있을 것이다.
•
편지는 영희가 먼저 발견할 수도, 영희네 오빠가 먼저 발견할 수도 있다, 그들은 가족이니까(Consumer Group).
•
만일 영희네 오빠가 편지를 먼저 발견한다면? 영희는 편지의 존재조차 모를 것이다.
Redis의 Pub/Sub
Channel은 이벤트를 저장하지 않는다. 만일 Channel에 이벤트가 도착했을 때, 해당 채널의 Subscriber가 존재하지 않는다면, 이벤트는 사라진다.
•
Redis의 Channel은 말 그대로, TV의 Channel을 생각하면 된다.
•
하루 종일 TV에서는 수백 개의 채널에서 방송이 방영된다. 각 방송사(Publisher)에서 방영하는 라이브 방송은, 해당 채널을 시청 중일 때만 볼 수 있다.
•
또한 같은 시간대에 같은 채널의 시청자(Subscriber)들은 모두 같은 방송을 볼 수 있다.
요약
•
기본 개념: Pub/Sub 모델에서는 메시지가 특정 주제(topic)에 게시(publish)되며, 이 주제를 구독(subscribe)한 모든 구독자(subscribers)가 메시지를 받습니다.
•
사용 사례: 이벤트 알림, 실시간 정보 업데이트, 광범위한 시스템 통합 등에 적합합니다.
•
특징: 메시지는 한 번에 여러 구독자에게 전달될 수 있으며, 이 모델은 일대다(one-to-many) 통신을 지원합니다. 송신자는 구독자가 누구인지 알 필요가 없으며, 구독자는 메시지의 출처를 명확히 알 필요가 없습니다.
Message queue vs Stream vs Pub/Sub
2개 방식 중 하나를 선택하는 핵심 기준은 "모든 consumer들이 모든 메세지를 전부 수신해야하는가?" 이다.
Message Queue | Streams | Pub/Sub | |
통신 모델 | 발행자(Publisher)는 큐에 메시지를 보낸다.
메시지는 큐에서 소비자(Consumer)에 의해 순차적으로 처리된다.
일반적으로 메시지는 하나의 소비자에게만 전달되며, 한 번 전달된 메시지는 큐에서 제거된다. | 발행자(Publisher)는 스트림에 이벤트를 보낸다.
이벤트는 시간 순서대로 스트림에 저장되며, 하나 이상의 소비자(Consumer)가 이벤트를 독립적으로 소비할 수 있다.
소비자는 스트림의 특정 지점부터 이벤트를 읽기 시작할 수 있으며, 이벤트는 소비 후에도 스트림에 남아 있다. | 발행자(Publisher)는 특정 주제(Topic)에 메시지를 게시한다.
구독자(Subscriber)는 자신이 관심 있는 주제에 대해 구독한다.
주제에 메시지가 게시되면, 해당 주제를 구독하는 모든 구독자에게 메시지가 전달된다. |
멀티 수신자 | 일반적으로 하나의 메시지는 하나의 소비자에게만 전달된다. | 하나의 이벤트 스트림은 여러 소비자에 의해 독립적으로 소비될 수 있다. 각 소비자는 자신의 속도로 이벤트를 처리한다. | 하나의 메시지는 여러 구독자에게 동시에 전달될 수 있다. |
메시지 지속성 | 메시지는 소비될 때까지 큐에 보관된다. | 이벤트는 설정된 기간 동안, 또는 저장소 용량에 따라 스트림에 지속적으로 저장된다. 이를 통해 소비자는 필요에 따라 과거 데이터를 조회하거나 재처리할 수 있다. | 구독자가 오프라인 상태일 때 메시지를 받지 못할 수도 있다.
그러나 Kafka와 같은 일부 시스템은 오프라인 구독자를 위한 메시지 지속성을 제공하기도 한다. |
용도 | 작업 분배, 비동기 처리, 워크플로우 실행 등에 적합하다. | 실시간 데이터 처리, 이벤트 소싱, 시계열 데이터 분석, 메시지 히스토리 관리 등에 적합하다. | 이벤트 알림, 로그 분석, 실시간 분석 등에 적합하다. |
결합도 | 발행자와 소비자 사이의 결합도가 상대적으로 높을 수 있다. | 발행자는 스트림에만 이벤트를 전송하며, 소비자가 누구인지, 어떻게 처리되는지 알 필요가 없다. 이는 발행자와 소비자 사이의 결합도를 낮춘다. | 발행자와 구독자 사이의 결합도가 낮다.
발행자는 메시지를 전달 받을 구독자를 알 필요가 없다. |