4장 분산 메시지 큐
- 메시지 큐 사용의 이점
- 결합도 완화(decoupling)
- 규모 확장성 개선: 소비자 시스템 규모를 트래픽 부하에 맞춰 독립적으로 늘릴 수 있다.
- 가용성 개선: 특정 컴포넌트에 장애가 발생해도 다른 컴포넌트는 서비스를 계속할 수 있다.
- 성능 개선: 메시지 큐를 통해 비동기 통신이 쉽게 가능하고 생산자와 소비자는 서로를 기다릴 필요가 없다.
메시지 큐 대 이벤트 스트리밍 플랫폼
- 아파치 카프카(Kafaka)나 펄사(Pulsa)는 엄밀히 말하면 메시지 큐가 아닌 이벤트 스트리밍 플랫폼이다.
- 데이터 장기 보관, 메시지 반복 소비 등의 부가 기능은 통상적으로 이벤트 스트리밍 플랫폼에서만 이용 가능하다.
- 하지만 메시지 큐(RocketMQ, RabbitMQ 등)의 기능이 점점 확장되며 경계가 점차 희미해지고 있다.
- ex) RabbitMQ에서 반복적으로 소비 가능하고, 장기 보관이 가능한 스트리밍 기능이 옵션으로 제공된다.
- 이번 장에선 이벤트 스트리밍 플랫폼이 지원하는 부가 기능을 갖춘 분산 메시지 큐를 설계해볼 것이다.
1단계: 문제 이해 및 설계 범위 확정
- 기능 요구사항
- 생산자는 메시지 큐에 메시지를 보낼 수 있다
- 소비자는 메시지 큐를 통해 메시지를 수신할 수 있다.
- 메시지는 반복적으로 수신할 수 있어야 하고, 단 한 번만 수신하도록 설정될 수 있어야 한다.
- 오래된 이력 데이터는 삭제될 수 있다.
- 메시지 크기는 킬로바이트 수준
- 메시지가 생산된 순서대로 소비자에게 전달할 수 있어야 한다.
- 메시지 전달 방식은 최소 한 번, 최대 한 번, 정확히 한 번 가운데 설정할 수 있어야 한다.
- 비기능 요구사항
- 높은 대역폭과 낮은 전송 지연 가운데 하나 선택 가능
- 규모 확장성 지원
- 지속성 및 내구성
- 데이터를 디스크에 지속적으로 보관
- 데이터를 여러 노드에 복제
2단계: 계략적 설계안 제시 및 동의 구하기
- 메시지 큐는 생산자와 소비자 사이 결합을 느슨하게 하는 서비스
- 생산자와 소비자의 독립적 운영 및 규모 확장을 가능하게 한다.
- 클라이언트/서버 모델 관점에서 생산자와 소비자는 모두 클라이언트고 메시지 큐는 서버 역할을 한다.
메시지 모델
- 일대일 모델
- 큐에 전송된 메시지는 오직 한 소비자만 소비
- 소비자가 많을 순 있지만 오직 한 소비자만 메시지를 가져갈 수 있다.
- 메시지가 소비되면 큐에 해당 메시지는 삭제된다. (데이터 보관을 지원하지 않음)
- 발행-구독 모델
- 토픽(topic)
- 메시지를 주제별로 정리하는 데 사용
- 각 토픽은 메시지 큐 서비스 전반에 고유한 이름을 가진다.
- 토픽에 전달된 메시지는 해당 토픽을 구독하는 모든 소비자에게 전달된다.
토픽, 파티션, 브로커
- 토픽 한 대로 감당하기 어려운 데이터 양이 쌓인다면 파티션(partition)을 활용할 수 있다.
- 파티션은 토픽에 보낼 메시지의 작은 부분 집합이다.
- 토픽을 여러 파티션으로 분할해 메시지를 모든 파티션에 균등하게 보낸다.
- 즉 토픽 용량을 확장하려면 파티션 개수를 늘리면 된다.
- 파티션을 유지하는 서버는 브로커(broker)라 부른다.
- 각 토픽 파텨신은 FIFO(first in, first out) 큐처럼 동작한다.
- 같은 파티션 안에서 메시지 순서가 유지된다.
- 같은 키를 가진 모든 메시지는 같은 파티션으로 보내진다.
- 토픽을 구독하는 소비자는 하나 이상의 파티션에서 데이터를 소비한다.
소비자 그룹
- 토픽을 구독하는 소비자가 여럿인 경우 이 소비자들을 소비자 그룹(consumer group)이라 부른다.
- 하나의 소비자 그룹은 여러 토픽을 구독할 수 있고 오프셋을 별도로 관리한다.
- 같은 그룹 내 소비자는 메시지를 병렬로 소비할 수 있다.
- 메시지 순서를 보장하려면 어떤 파티션의 메시지는 한 그룹 안에서 오직 한 소비자만 읽을 수 있도록 제약을 추가해야 한다.
- 다만 그룹 내 소비자 수가 구독하는 토픽의 파티션 수보다 크면 메시지를 받지 못하는 소비자가 생긴다.
- 그래서 아래 다이어그램에서 소비자-4은 토픽 B의 메시지를 수신할 수 없다.
graph TB
subgraph Topic A
A1[파티션-1]
A2[파티션-2]
end
subgraph Topic B
B1[파티션-1]
end
subgraph Consumer Group 1
C1_1[소비자-1]
C1_2[소비자-2]
end
subgraph Consumer Group 2
C2_1[소비자-3]
C2_2[소비자-4]
end
A1 --> C1_1
A2 --> C1_2
A1 --> C2_1
A2 --> C2_2
B1 --> C2_1
- 그래서 파티션 수를 동적으로 늘리는 일을 피하기 위해 충분한 파티션을 미리 할당해두기도 한다.
- 처리 용량을 늘리려면 소비자를 더 추가하면 된다.
계략적 설계안
- 클라이언트
- 생산자: 메시지를 특정 토픽에 전송
- 소비자 그룹: 토픽을 구독하고 메시지를 소비
- 핵심 서비스 및 저장소
- 브로커: 파티션 유지
- 저장소
- 데이터 저장소: 메시지는 파티션 내 저장소에 보관
- 상태 저장소: 소비자 상태를 저장
- 메타데이터 저장소: 토픽 설정, 토픽 속성 등 저장
- 조정 서비스
- 서비스 디스커버리: 어떤 브로커가 살아있는지 탐색
- 리더 선출: 브로커 가운데 하나를 리더로 선정해야 하며 한 클러스터에는 반드시 활성 리더가 하나 있어야 한다. 이 리더가 파티션 배치를 책임진다.
- 아파치 주키퍼나 etd가 보텅 리더 선출 컴포넌트로 유명하다.
3단계: 상세 설계
데이터 저장소
- 메시지를 지속적으로 저장할 저장소 선택을 위해 메시지 큐 트래픽 패턴을 살펴보자
- 읽기/쓰기가 빈번히 발생
- 갱신/삭제 연산은 발생하지 않는다.
- 순차적인 읽기/쓰기가 대부분이다.
- 선택지 1: 데이터베이스
- 관계형 DB라면 토픽별로 테이블을 만들어 메시지를 레코드로 추가하는 방법이 있다.
- NoSQL이라면 토픽별로 컬렉션을 만들어 메시지를 하나의 문서로 추가하면 된다.
- 다만 읽기와 쓰기가 동시에 대규모로 빈번하게 발생하는 상황을 잘 처리하는 데이터베이스를 설계하기는 어렵다.
- 선택지 2: 쓰기 우선 로그 (Write-Ahead Log, WAL)
- WAL은 데이터가 추가되기만 하는 (append-only) 일반 파일이다.
- MySQL의 redo log가 WAL로 구현되어 있다.
- WAL에 대한 접근 패턴은 읽기/쓰기 전부 순차적이고 순차적인 접근에서 아주 좋은 성능을 보인다.
- 지속성을 보장해야 한다면 메시지는 디스크에 WAL로 보관할 것을 추천한다.
- 디스크 성능 관련 유의사항
- 데이터 장기 보관 요구사항 때문에 디스크 드라이버로 다량의 데이터를 보관할 수 있다.
- 회전식 디스크가 느린건 랜덤한 접근일 때고, 순차적 데이터 접근 패턴이라면 현대적 디스크 드라이버에서 수백 MB/sec 성능을 달성하는 것은 어렵지 않다.
- 또한 현대 운영체제는 디스크 데이터를 메모리에 적극적으로 캐시한다.
메시지 자료 구조
- 메시지 자료 구조는 생산자, 메시지 큐, 소비자 사이의 계약(contract)이다.
- 메시지가 소비자에게 전달되는 과정에서 불필요한 복사를 피하면 높은 대역폭을 달성할 수 있다.
- 컴포넌트 가운데 게약을 그대로 받아들이지 못하는 것이 있다면 메시지 변경 과정에서 값비싼 복사가 발생
- 메시지 키
- 키는 파티션을 정할 때 사용되고 문자열일 수도 숫자일 수도 있다.
- 키가 주어지지 않으면 무작위로 파티션이 결정된다.
- 키가 주어진다면 파티션은
hash(key) % numPartitions
공식으로 결정된다.
- 이는 직접 정의할 수도 있는데 모든 파티션에 메시지가 균등히 분산되도록 해야 한다.
- 메시지 값은 일반 텍스트일 수도 있고 압축된 이진 블록일 수도 있다.
- 메시지의 기타 필드
- 토픽: 메시지가 속한 토픽 이름
- 파티션: 메시지가 속한 파티션 ID
- 오프셋: 파티션 내 메시지의 위치
- 메시지는 토픽, 파티션, 오프셋 세 가지만 알면 찾을 수 있다.
- 타임스탬프
- 크기
- CRC: 순환 중복 검사(Cyclic Redundancy Check)로 주어진 데이터의 무결성 보장에 이용된다.
일괄 처리
- 생산자, 소비자, 메시지 큐는 메시지를 가급적 일괄 처리한다.
- 일괄 처리는 성능 개선에 중요한 역할을 한다.
- OS로 하여금 여러 메시지를 한 번의 네트워크 요청으로 전송할 수 있게 한다.
- 브로커가 여러 메시지를 한 번에 로그에 기록하면 더 큰 규모의 순차 쓰기 연산이 발생한다.
- 그럼 OS가 관리하는 디스크 캐시에 더 큰 규모의 연속된 공간을 점유하게 되어 더 높은 디스크 접근 대역폭을 달성할 수 있다.
- 하지만 높은 대역폭과 낮은 응답 지연은 동시에 달성하기 어렵다.
- 낮은 응답 지연이 중요하다면 일괄 처리 메시지 양을 낮춰야 하지만 디스크 성능은 다소 낮아진다.
- 처리량을 높여야 한다면 토픽당 파티션 수를 늘려야 낮아진 순차 쓰기 연산 대역폭을 벌충할 수 있다.
생산자 측 작업 흐름
- 생산자가 보내는 메시지를 알맞은 파티션에 보내기 위해 라우팅 계층이 필요하다.
- 라우팅 계층은 적절한 브로커에 메시지를 보내는 역할을 담당한다.
- 브로커는 장애 감내를 위해 여러 개로 복제되어 운용되기에 라우팅 계층은 리더 브로커에게 메시지를 전송해야 한다.
- 하지만 라우팅 계층을 도입하면서 거쳐야 할 네트워크 노드가 하나 더 늘어나기에 전송 지연이 발생할 수 있다.
- 전송 지연 해결을 위해 라우팅 계층을 생산자 내부로 편입 시키고 버퍼를 도입할 수 있다.
- 네트워크를 거칠 필요가 없어 전송 지연이 줄어든다.
- 생산자는 메시지를 어느 파티션에 보낼지 자신만의 로직을 가질 수 있다.
- 전송할 메시지를 버퍼 메모리에 보관했다가 목적지로 일괄 전송하여 대역폭을 높일 수 있다.
- 얼마나 많은 메시지를 일괄 처리하는 것이 좋을지는 대역폭과 지연 사이에 균형을 찾아야 한다.
소비자 측 작업 흐름
- 소비자는 특정 파티션의 오프셋을 주고 해당 위치에서부터 이벤트를 묶어 가져온다.
- 푸시 모델
- 보로커가 소비자에게 데이터를 전송
- 장점
- 낮은 지연: 브로커가 메시지를 받는 즉시 소비자에게 보낼 수 있다.
- 단점
- 소비자가 메시지를 처리하는 속도가 메시지를 푸시하는 속도보다 느릴 경우 소비자에게 큰 부하가 생길 수 있다.
- 생산자가 데이터 전송 속도를 좌우하기에 소비자는 그에 맞게 컴퓨팅 자원을 준비해 둬야 한다.
- 풀 모델
- 소비자가 브로커로부터 메시지를 요청
- 장점
- 메시지 소비 속도를 소비자가 결정할 수 있다.
- 메시지 소비 속도가 생산 속도보다 느려지면 소비자를 늘리거나 소비자가 기다려도 된다.
- 소비자는 오프셋 이후의 메시지를 한 번에 가져갈 수 있기에 일괄 처리에 적합하다.
- 단점
- 브로커에 메시지가 없어도 소비자는 메시지를 가져가려고 하기에 컴퓨팅 자원이 낭비된다.
- 이를 극복하기 위해 롤 폴링 모드를 사용할 수도 있다.
- 대부분의 메시지 큐는 풀 모델을 지원한다.
- 특정 소비자 그룹에 합류하고 특정 토픽을 구독하려는 소비자는 해당 소비자 그룹의 코디네이터(브로커)에 접속한다.
- 코디네이터는 소비자를 그룹에 참여시키고 파티션을 할당한다.
- 소비자는 상태 저장소에 저장되어 있는 마지막으로 소비한 오프셋 이후 메시지를 가져온다.
- 소비자는 메시지를 처리하고 새로운 오프셋을 브로커에 보낸다.
소비자 재조정
- 소비자 재조정은 어떤 소비자가 어떤 파티션을 책임지는지 다시 정하는 프로세스다.
- 새로운 소비자가 합류하는 경우
- 기존 소비자가 그룹을 떠나는 경우
- 어떤 소비자에 장애가 발생하는 경우
- 파티션들이 조정되는 경우
- 코디네이터는 소비자 재조정을 위해 소비자들과 통신하는 브로커 노드이다.
- 소비자로부터 오는 heartbeat 메시지와 오프셋 정보를 관리
- 각 소비자 그룹엔 전담 코디네이터가 있어 같은 그룹의 모든 소비자는 코디네이터에 연결한다.
- 코디네이터는 자신에 연결한 소비자 목록을 유지한다.
- 소비자 목록에 변화가 생기면 새 리더가 선출되고 이를 다른 모든 소비자에게 알린다.
- 분산 시스템에서 코디네이터는 소비자로부터 오는 박동 신호를 통해 장애를 감지하고 재조정 프로세스를 시작한다.
상태 저장소
- 메시지 큐 브로커의 상태 저장소엔 다음 정보가 저장된다.
- 소비자에 대한 파티션의 배치 관계
- 각 소비자 그룹이 각 파티션에서 마지막으로 가져간 메시지의 오프셋
- 특정 소비자에 장애가 생기면 같은 그룹의 새로운 소비자가 상태 저장소에 저장된 오프셋을 활용해 다음 위치부터 메시지를 읽을 수 있다.
- 소비자 상태 정보 데이터가 이용되는 패턴은 다음과 같다.
- 읽기와 쓰기가 빈번하게 발생하지만 양은 많지 않다.
- 데이터 갱신은 빈번하게 일어나지만 삭제되는 일은 거의 없다.
- 읽기와 쓰기 연산은 무작위적 패턴을 보인다.
- 데이터 일관성이 중요하다.
- 카프카는 오프셋 저장소로 주키퍼를 사용하다가 카프카 브로커로 이전하였다.
메타데이터 저장소
- 메타데이터 저장소에는 토픽 설정이나 속성 정보를 보관한다.
- 파티션 수, 메시지 보관 기간, 사본 배치 정보 등
- 메타데이터는 자주 변경되지 않고 양도 적지만 높은 일관성을 유지하기에 주키퍼가 적절하다.
주키퍼
- 주키퍼는 계층적 키-값 저장소(hierarchical key-value store) 기능을 제공하는 분산 시스템에 필수적인 서비스다.
- 주키퍼는 분산 시스템에 필수적인 서비스로 분산 메시지 큐 설계에도 아주 유용하다.
- 메타데이터와 상태 저장소는 주키퍼를 이용해 구현
- 브로커는 메시지 데이터 저장소만 유지
- 주키퍼가 브로커 클러스터의 리더 선출 과정을 돕는다.
복제
- 장애를 대비한 높은 가용성을 보장하기 위해 복제(replication)이 필요하다.
- 각 파티션은 n개의 사본을 가질 수 있고 사본들은 서로 다른 브로커 노드에 분산될 수 있다.
- 생산자는 리더 파티션에게만 메시지를 보낸다.
- 사본은 리더로부터 새 메시지를 지속적으로 가져와 동기화한다.
- 메시지를 동기화한 사본 개수가 임계값을 넘으면 생산자에게 메시지를 받았다는 응답을 보낸다.
- 사본 분산 계획(replica distribution plan)
- 사본을 파티션에 어떻게 분산할지 기술하는 것
- ex) 토픽 A의 파티션-1은 사본 3개, 리더는 브로커 1, 단순 사본은 브로커 2와 3에 배치
- 사본 분산 계획은 조정 서비스가 선정한 리더 브로커 노드가 만들어 메타데이터 저장소에 보관한다.
사본 동기화
- 동기화된 사본(In-Sync Replicas, ISR) - 리더와 동기화된 사본을 일컫는 용어
- ‘동기화되었다’는 의미는 토픽 설정에 따라 달라진다.
- ex) replica.lag.max.messages 값이 4인 경우 사본에 보관된 메시지 개수와 리더 사이의 차이가 3이라면 해당 사본은 여전히 ISR이다. (리더는 항상 ISR 상태)
- 합의 오프셋(committed offset)
- 이 오프셋 이전에 기록된 메시지는 이미 ISR 집합 내 모든 사본에 동기화가 끝났다는 것
- ISR 동작 방식
- 합의 오프셋 값이 13이고, 리더가 15 오프셋까지 메시지를 받은 경우
- 적어도 13 오프셋 메시지까지 동기화한 사본은 ISR로 간주된다.
- 13 미만인 오프셋 메시지까지만 동기화한 사본은 ISR이 아니다.
- ISR이 필요한 이유
- ISR은 성능과 영속성 사이의 타협점이다.
- 메시지를 소실하지 않으려면 모든 사본을 동기화하고 생산자에게 응답을 보내는 것이다.
- 하지만 어느 사본 하나라도 동기화를 신속하게 하지 못하게 되면 성능에 문제가 발생할 것이다.
- 생산자는 k개 ISR이 메시지를 받았을 때 응답을 받도록 k 값을 설정할 수 있다.
- ACK=all
- 생산자는 모든 ISR이 메시지를 수신한 뒤에 ACK 응답을 받는다.
- 응답 받는 속도가 느리지만 영속성 측면에선 가장 좋다.
- ACK=1
- 리더가 메시지를 저장하고 나면 바로 ACK 응답을 받는다.
- 응답 지연은 개선되지만 리더 장애 시 메시지가 소실될 가능성이 매우 높다.
- 데이터가 사라져도 상관 없지만 낮은 지연이 필요할 때 적합하다.
- ACK=0
- 생산자가 메시지에 대한 ACK를 기다리지 않고 계속 메시지를 전송하며 어떤 재시도도 하지 않는다.
- 지표 수집이나 데이터 로깅 등에 사용된다.
- 소비자 입장에서 가장 쉬운 구성은 리더에서 메시지를 읽는 것이다.
- 설계 및 운영이 단순
- 특정 파티션의 메시지는 같은 수비자 그룹 내에 오직 한 소비자만 읽어가기에 리더에 대한 연결은 많지 않다.
- 인기 있는 토픽이 아니라면 리더 사본에 대한 연결 수는 많지 않다.
- 아주 인기 있는 토픽이라면 파티션 및 소비자 수를 늘려 확장 가능하다.
- 만약 소비자와 리더 사본이 다른 데이터 센터에 있는 경우 지역적으로 가까운 ISR 사본에서 메시지를 읽는 선택지를 고려해볼 수도 있다.
- 각 파티션 담당 리더는 사본들의 ISR 목록을 관리하기에 ISR을 식별할 수 있다.
규모 확장성
- 생산자
- 생산자의 규모 확장성은 새로운 생산자를 추가하거나 삭제함으로써 쉽게 달성 가능하다.
- 소비자
- 소비자 그룹은 서로 독립적이기에 새 소비자 그룹은 쉽게 추가/삭제가 가능하다.
- 같은 소비자 그룹 내 소비자가 추가/삭제 되는 경우엔 재조정 메커니즘이 필요하다.
- 소비자 그룹과 재조정 메커니즘이 소비자 측의 규모 확장성과 결함 내성을 보장한다.
- 브로커
- 브로커의 결함 내성을 높이기 위한 고려 사항
- 사본에 메시지가 얼마나 반영되어야 하는지 응답 지연과 안정성 사이 균형을 찾아야 한다.
- 파티션의 모든 사본은 같은 노드에 두지 말고 다른 노드로 분산시켜야 브로커 장애 시 파티션 완전 소실을 막을 수 있다.
- 서로 다른 데이터 센터에 파티션 사본을 두면 안정적이지만 동기화로 인한 응답 지연이 발생할 수도 있고 비용도 증가한다.
- 데이터 미러링을 도입해 데이터 센터 간 복사를 용이하게 하는 것도 방법이다.
- 브로커 노드가 추가/삭제될 때 사본을 재배치하는 것도 방법이지만 브로커 컨트롤러로 하여금 한시적으로 시스템에 설정된 사본 수보다 많은 사본을 허용하도록 하는 방법이 있다.
- 최초 구성: 3개 브로커, 2개 파티션, 파티션당 3개의 사본이 있는 상황
- 새로운 브로커-4가 추가되고 브로커 컨트롤러는 파티션-2의 사본 분산 계획을 2, 3, 4로 변경한다는 결정을 내린다.
- 브로커-4에 추가된 파티션-2의 새로운 사본은 파티션-2의 리더가 있는 브로커로부터 메시지를 동기화하기 시작한다. (이 때 파티션-2의 사본 개수는 한시적으로 4개)
- 브로커-4의 사본이 리더 상태를 완전히 따라잡으면 브로커-1에 있던 파티션-2의 사본 1개를 삭제한다.
- 파티션
- 운영상 이유로 파티션 수를 조정해야 하는 일이 생길 수 있다.
- 토픽 규모가 늘어나는 경우, 대역폭을 조정하는 경우 등
- 파티션 수의 조정은 생산자와 소비자 사이의 안정성에는 영향을 주지 않는다.
- 파티션이 추가되는 경우는 단순한데 추가된 이후에 오는 메시지가 새 파티션에 지속적으로 보관되면 된다.
- 파티션 삭제는 조금 까다롭다.
- 파티션이 퇴역된다는 결정이 내려지면 새로운 메시지는 다른 파티션에만 보관된다.
- 퇴역된 파티션은 바로 제거되지 않고 일정 시간 유지하는데 해당 파티션 데이터를 읽는 소비자가 있을 수 있기 때문이다.
- 실제로 파티션이 제거되는 시점에 생산자 그룹은 재조정 작업을 개시해야 한다.
메시지 전달 방식
- 최대 한 번
- 메시지를 최대 한 번만 전달하면서 소실되더라도 재시도하지 않는다.
- 생산자는 비동기적으로 메시지를 보내고 응답을 기다리지 않는다.
- 소비자는 메시지를 읽고 처리하기 전에 오프셋부터 갱신한다.
- 지표 모니터링 등 소량의 데이터 손실은 감수할 수 있는 애플리케이션에 적합하다.
- 최소 한 번
- 같은 메시지가 한 번 이상 전달될 수 있으나 소실은 발생하지 않는 전달 방식
- 소비자는 데이터를 성공적으로 처리한 뒤에만 오프셋을 갱신하기에 처리 실패 시엔 다시 메시지를 가져오려고 한다.
- 소비자가 메시지를 처리했지만 미쳐 오프셋을 갱신하지 못한 경우 메시지가 중복 처리될 수도 있다.
- 데이터 중복이 큰 문제가 아닌 애플리케이션이면 괜찮은 방식이다.
- 정확히 한 번
- 시스템 성능 및 구현 복잡도가 매우 크지만 사용자 입장에선 편리하다.
- 지불, 매매, 회계 등 금융 관련 시스템에 적합한 방식
메시지 필터링
- 토픽은 같은 유형의 메시지를 담아 처리하기 위한 논리적 개념이지만 특정 소비자 그룹은 그 가운데서도 특정한 메시지에만 관심이 있는 경우가 있다.
- ex) Order 시스템은 토픽에 주문 관련 모든 메시지를 전송하지만 Payment 시스템은 그 가운데 결제나 환불 관련 메시지에만 관심 있을 것이다.
- 토픽을 분리하는 것도 방법이지만 다음과 같은 우려가 있다.
- 이런 상황이 있을 때마다 토픽을 분리할 것인가?
- 같은 메시지를 여러 토픽에 저장하는 것은 낭비
- 새로운 소비자 측 요구사항이 등장할 때마다 생산자 구현을 바꿔야할 수도 있다.
- 메시지를 필터링하여 이 문제를 해결할 수 있다.
- 모든 메시지를 받고 소비자가 필요 없는 메시지를 버리면 되지만 이는 불필요한 트래픽을 유발한다.
- 브로커에서 메시지를 필터링하여 소비자가 원하는 메시지만 받을 수 있도록 할 수 있지만 필터링을 위해 복호화나 역직렬화가 필요하다면 브로커 성능이 저하된다.
- 따라서 브로커는 메타데이터 영역의 태그 등을 읽어 메시지를 필터링한다면 큰 무리 없이 메시지를 필터링할 수 있다.
메시지의 지연 전송 및 예약 전송
- 때로 소비자에게 보낼 메시지를 일정 시간만큼 지연시켜야 하는 경우가 있다.
- ex) 주문 후 30분 안에 결재가 이루어지지 않으면 주문을 취소하고 싶은 경우
- 이런 메시지는 토픽에 바로 저장하지 않고 브로커 내부 임시 저장소에 넣었다가 시간이 되면 토픽으로 옮긴다.