Pub-Sub & Fan-out Mechanics
하나의 메시지를 여러 구독자에게 동시에 전달하는 발행/구독 모델의 원리와 대규모 트래픽 분산을 위한 팬아웃 수리 메커니즘을 다루는 학습 노드입니다.
Article
M
Me
hyunyoun's Blog
network-communicationnetworkcommunicationdistributed-messagingevent-streamingpub-subfan-out-mechanicslearning10 min read
1. Overview
펍섭과 팬아웃 역학(Pub-Sub & Fan-out Mechanics)은 하나의 메시지를 여러 소비자에게 동시에 뿌려야 할 때, 애플리케이션 코드가 수십 번 반복해서 메시지를 복사하는 바보짓을 멈추고 네트워크 인프라 레벨에서 단 한 번의 전송으로 트래픽을 증폭시키는 라우팅 마법을 해부합니다.
학습자는 A가 B에게 몰래 쪽지를 건네는 '1<1>1> 통신(Point-to-Point)'의 한계를 넘어서, 사내 방송국처럼 모두에게 외치는 발행-구독(Pub/Sub) 아키텍처의 공간적 디커플링을 뜯어봅니다. 나아가 RabbitMQ의 뇌 역할을 하는 **익스체인지(Exchange)**를 통해, 묻지도 따지지도 않고 복사해 주는 Fanout, 특정 키워드만 쏙쏙 골라 배달하는 Direct와 Topic 라우팅의 정교한 분기 처리를 장악합니다. 마지막으로, 생산자(Producer)의 송신 비용을 에서 로 박살 내어 백엔드 서버의 CPU와 네트워크 대역폭을 보존하는 인프라 오프로딩(Offloading) 역량을 확보합니다.
2. Scope & Boundaries
In-Scope
- Pub/Sub Pattern: 1
메시지 전달 모델, Publisher와 Subscriber의 물리적/논리적 분리. - AMQP Exchanges: RabbitMQ의 라우팅 컴포넌트 (Direct, Fanout, Topic, Headers).
- Fan-out Architecture: 메시지 복제 및 분배를 애플리케이션이 아닌 브로커(인프라)가 담당하는 구조.
- Routing Key & Binding: 익스체인지와 큐(Queue)를 연결하는 규칙(Binding)과 필터링(Routing Key).
Out-of-Scope
- Kafka의 Pub/Sub 메커니즘: 카프카가 컨슈머 그룹(Consumer Group)을 통해 Pub/Sub을 흉내 내는 방식 08-05-02 Event Streaming & Kafka 영역에서 기학습.
- 웹 브라우저 클라이언트 푸시: 클라이언트에게 실시간으로 브로드캐스팅하는 기술 08-04-03 WebSocket & SSE 영역으로 분리.
Boundaries
- Point-to-Point vs Pub/Sub: Point-to-Point(1<1>1>) 큐는 100만 원 입금 메시지를 '회계 서버' 혼자 먹고 끝냅니다. 다른 서버는 못 봅니다(소비적). 반면 Pub/Sub(1
)은 입금 메시지를 '회계 서버', '알림 서버', '통계 서버' 3곳이 동시에 복사해서 가져갑니다(비소비적 브로드캐스트). 전자는 일감 분배(Work Queue)에 쓰이고, 후자는 이벤트 전파(Event Notification)에 쓰인다는 도메인 역할의 차이를 명확히 경계 짓습니다.
3. Counterexample
- 애플리케이션 레벨 팬아웃의 CPU 학대: "회원 가입 완료" 이벤트를 메일 서버, 쿠폰 서버, 통계 서버 3곳에 전달해야 합니다. 개발자가 회원가입 서버 코드에
sendToMailQueue(),sendToCouponQueue(),sendToStatQueue()함수를 3번 연속으로 호출하도록 짰습니다. 나중에 알림 서버, 추천 서버가 10개로 늘어나자, 회원가입 서버는 1명의 유저가 가입할 때마다 네트워크로 메시지를 10번씩 쏘느라 CPU와 대역폭(Bandwidth)이 터져나갔습니다. 브로커의Fanout Exchange를 써서 1번만 쏘면 브로커가 10번 복사해 주는 인프라 확장을 몰랐던 처참한 하드코딩입니다. - 무지성 브로드캐스트의 큐 범람: 시스템의 모든 로그(INFO, WARN, ERROR)를
Fanout익스체인지로 때려 박았습니다. 에러 로그만 보고 싶은 '알람 서버'의 큐에도 하루 1억 건의 INFO 로그가 같이 쏟아져 들어옵니다. 알람 서버는 쓸데없는 INFO 로그를 꺼내서 버리는(Drop) 헛수고를 하다가 진짜 ERROR 로그를 제때 처리하지 못하고 뻗어버렸습니다. 패턴 매칭(*.ERROR)으로 필요한 것만 라우팅해 주는Topic Exchange를 쓰지 않아 발생한 끔찍한 네트워크 낭비입니다.
4. Prerequisites
- Message Broker 기본 (Basic): 큐(Queue)와 비동기 통신. (08-05-01 Broker Physics)
- 이벤트 기반 아키텍처 (Basic): 마이크로서비스 간의 이벤트 전파. (07-03-01 EDA)
5. Learning Map
6. Learning Topics
Basic
Core Topic 01: 허공에 외치기 (The Pub/Sub Paradigm)
- Why to Learn: 메시지를 보낼 때 "저기 있는 알림 서버 큐에 넣어줘"라고 목적지를 지정(Point-to-Point)하는 낡은 결합을 버리고, 누가 듣든 말든 일단 방송(Publish)하고 보는 철저한 독립성을 통찰하기 위함입니다.
- What to Learn:
- Concepts: Publisher(발행자), Subscriber(구독자), Spatial Decoupling(공간적 디커플링), Event Notification.
- Skills: 결제 완료 비즈니스 로직에서 '이메일 발송', '재고 차감' 등의 후속 작업(Subscriber)이 몇 개가 추가되든 결제 서버(Publisher)의 코드는 단 한 줄도 수정되지 않는 아키텍처 설계.
- How to Learn:
- 1단계: 공간적 결합 (Point-to-Point): A가 B의 큐 이름을 정확히 알아야만 보낼 수 있습니다. C라는 새로운 서버가 추가되어 같은 데이터를 원하면, A의 코드를 뜯어고쳐 C의 큐 이름도 추가해 줘야 합니다(OCP 위배).
- 2단계: 허공의 메아리 (Pub/Sub): A는 이제 B나 C의 존재를 아예 모릅니다. A는 그냥 브로커의 로비(Exchange)에 "결제 1건 발생!" 하고 확성기로 외치고 뒤돌아섭니다. 이 소식을 듣고 싶은 B와 C는 브로커에게 미리 "결제 소식 나오면 나한테 복사해 줘(Subscribe)"라고 신청해 둡니다. A의 코드 변경 없이 D, E 서버를 무한대로 붙일 수 있는 공간적 해방을 뜯어봅니다.
- Implement: Publisher-Subscriber 결합도 테스트 시뮬레이션.
Publisher:publish('PAYMENT_DONE', {id: 1}). (큐 이름 모름).Sub_1(Mail):bind('PAYMENT_DONE').Sub_2(Stock):bind('PAYMENT_DONE'). 시스템 가동 중Sub_3(Stats)가 새로 추가되어bind를 걸어도,Publisher프로세스는 재시작이나 코드 수정 없이 즉각 3곳으로 데이터가 퍼져나가는(Broadcast) OCP(Open-Closed Principle) 충족 시각화.
Recommended
Core Topic 02: 복제술의 미학, 팬아웃 (Fan-out Exchange)
- Why to Learn: 앱 1개가 네트워크 통신을 10번 하던 의 발송 오버헤드를, 브로커가 대신 복제해 주는 로 밀어버려 백엔드 서버의 성능을 극대화하기 위함입니다.
- What to Learn:
- Concepts: AMQP Exchange, Fanout Exchange, Binding, Message Cloning, Network Amplification.
- Skills: 여러 부서가 동시에 받아봐야 하는 '전사 공지사항' 데이터를 쏠 때, RabbitMQ의 Fanout 익스체인지를 설정하여 묶여있는(Bound) 모든 큐에 조건 없이 복사해 넣는 파이프라인 구축.
- How to Learn:
- 1단계: AMQP 익스체인지(Exchange): RabbitMQ는 메시지를 큐에 바로 꽂지 않습니다. 무조건 '익스체인지'라는 우체국 분류 센터를 먼저 거칩니다.
- 2단계: 무지성 복사 (Fanout): 익스체인지 타입을
Fanout으로 설정합니다. 여기에 큐 3개(A, B, C)를 연결(Binding)해 둡니다. 생산자가 메시지 1개를 이 익스체인지에 딱 던집니다. 익스체인지는 내용이나 꼬리표를 묻지도 따지지도 않고, 메시지를 3개로 복제해서 A, B, C 큐에 쑤셔 넣습니다. 생산자의 네트워크 카드(NIC)는 단 1번만 패킷을 쏘고, 나머지 3번의 증폭은 빵빵한 브로커 서버가 대신해 주는 인프라 오프로딩을 해부합니다.
- Implement: Fanout Network I/O 절감 계산기.
메시지 크기 1MB. 구독자 수 100개(서버 100대).
App-level Fanout: 앱 서버가 1MB를 100번 쏨Outbound 100MB소모, 대역폭 터짐.Broker-level Fanout: 앱 서버가 브로커에 1MB 딱 1번 쏨Outbound 1MB. 브로커가 내부 메모리에서 복제하여 100MB로 증폭 발송. 앱 서버의 네트워크 부하를 1/100로 박살 내는 렌더링.
Practical
Core Topic 03: 표적 저격, 다이렉트 라우팅 (Direct Exchange)
- Why to Learn: 무조건 다 복사해 주는 Fanout의 낭비를 막고, 내가 쏜 메시지가 "정확히 이 조건을 원하는 큐"에만 꽂히도록 라우팅 룰을 튜닝하기 위함입니다.
- What to Learn:
- Concepts: Direct Exchange, Routing Key, Binding Key, Exact Match, Unicast / Multicast.
- Skills: 로그(Log)를 수집할 때
ERROR라우팅 키를 단 메시지만 알람 서버 큐로 보내고,INFO키를 단 메시지는 무시하거나 보관 서버 큐로만 꽂히게 하는 분기(Switching) 로직 작성.
- How to Learn:
- 1단계: 꼬리표 (Routing Key): 생산자가 메시지를 익스체인지에 던질 때 겉면에 포스트잇(Routing Key)을 붙입니다. 예:
routing_key="ERROR". - 2단계: 정확한 매칭 (Direct): 익스체인지 타입을
Direct로 설정합니다. A 큐는binding_key="ERROR"로 연결하고, B 큐는binding_key="INFO"로 연결합니다. 익스체인지는 포스트잇에 적힌 글씨와 큐의 결속(Binding) 글씨가 완벽히 똑같은(Exact Match) 곳으로만 메시지를 던집니다. 즉ERROR쪽지가 오면 A 큐로만 가고 B 큐로는 안 갑니다. 불필요한 큐에 쓰레기 데이터가 쌓이는 걸 막아내는 세미 브로드캐스트(Targeting)를 뜯어봅니다.
- 1단계: 꼬리표 (Routing Key): 생산자가 메시지를 익스체인지에 던질 때 겉면에 포스트잇(Routing Key)을 붙입니다. 예:
- Implement: Direct 라우팅 로직 모사 (Python).
ExchangeType = DIRECT.Queue_Alarm바인딩:[ERROR, FATAL].Queue_Storage바인딩:[INFO, WARN, ERROR, FATAL]. 수신 메시지Msg(RoutingKey="WARN")Queue_Alarm은 무시됨,Queue_Storage에만 들어감. 수신 메시지Msg(RoutingKey="FATAL")두 큐 모두에 들어감(Multicast). 유연한 필터링 차트 시각화.
Advanced
Core Topic 04: 정규식의 마법, 토픽 익스체인지 (Topic Exchange)
- Why to Learn: Direct 방식의 1<1>1> 텍스트 매칭을 넘어, 계층화된 문자열과 와일드카드(
*,#)를 조합해 무한대의 다이내믹 라우팅 룰을 브로커 단에서 구현하는 극강의 유연성을 장악하기 위함입니다. - What to Learn:
- Concepts: Topic Exchange, Hierarchical Routing Key (
a.b.c), Wildcards (*한 단어 매치,#0개 이상 다중 단어 매치). - Skills:
device.seoul.sensor1,device.busan.sensor2처럼 날아오는 수백만 개의 IoT 데이터 중, 서울 지역의 모든 장비(device.seoul.*) 데이터만 모으는 큐를 동적으로 바인딩하기.
- Concepts: Topic Exchange, Hierarchical Routing Key (
- How to Learn:
- 1단계: 계층형 꼬리표: 라우팅 키를 점(
.)으로 구분해서 만듭니다.[카테고리].[지역].[장비]. 예:log.asia.server1. - 2단계: 와일드카드 (Topic): 익스체인지 타입을
Topic으로 바꿉니다. A 큐는 아시아 전체 로그를 원하므로log.asia.*로 바인딩합니다(*는 딱 한 단어 매칭). B 큐는 지역 상관없이 모든 로그를 다 원하므로log.#으로 바인딩합니다(#은 뒤에 몇 단어가 오든 다 매칭). - 3단계: 만약
log.asia.server1메시지가 오면 A 큐와 B 큐 둘 다 들어갑니다. 하지만log.europe.server2가 오면 A 큐는 거르고 B 큐에만 들어갑니다. 애플리케이션 코드에 복잡한if/else정규식을 짤 필요 없이, 브로커 인프라가 알아서 트래픽을 분류하고 필터링해 주는 예술적 라우팅을 해부합니다.
- 1단계: 계층형 꼬리표: 라우팅 키를 점(
- Implement: Topic Exchange 패턴 매칭 엔진 모사.
입력 메시지
RoutingKey = "kr.seoul.gangnam.temp".Queue_1 Binding = "kr.*.*.temp"Match OK(별이 딱 2개).Queue_2 Binding = "kr.#"Match OK(하위 모두 허용).Queue_3 Binding = "kr.seoul.*"Match FAIL(별 1개인데 단어는 2개 남음). 문자열 토크나이저(Tokenizer)가 인프라 레벨에서 트래픽을 가려내는 고속 정규식 렌더링.
7. Terminology
8. References
Primary
- [P1] CS2023 - Software Engineering (SE) - Event-Driven and Asynchronous Architectures
- [P5] SFIA - Systems Integration (SINT) - Pub/Sub and Messaging Topologies
Secondary
- [Enterprise Integration Patterns] Gregor Hohpe - Publish-Subscribe Channel, Routing Slip
- [RabbitMQ in Action] Alvaro Videla - Exchanges, Routing, and Beyond
Industry
- [RabbitMQ Documentation] - Tutorials: Publish/Subscribe, Routing, Topics
- [AWS Architecture Blog] - Fanout SQS message routing with SNS
9. Final Checklist
Primary
- 생산자(Publisher)가 수신자(Subscriber)의 주소나 큐 이름을 직접 알 필요 없이 이벤트를 발행함으로써, 시스템이 거대해져도 생산자 코드가 단 한 줄도 수정되지 않는(OCP) 공간적 디커플링의 원리를 설명할 수 있는가?
- 메시지를 10곳으로 보내기 위해 애플리케이션 단에서
send()를 10번 호출하는 병목을, 브로커의Fanout Exchange를 이용해 의 송신 비용으로 덜어내는(Offloading) 아키텍처적 이점을 증명할 수 있는가?
Secondary
-
Direct Exchange를 사용하여 수만 건의 로그 트래픽 중 Routing Key가 정확히"ERROR"와 일치(Exact Match)하는 메시지만 특정 알람 큐로 스위칭(Switching)하여 불필요한 큐 범람을 막아낼 수 있는가? - 계층형 라우팅 키 체계(예:
region.service.level)에서Topic Exchange의 와일드카드(*,#)를 활용하여, 아시아 지역의 모든 경고 로그(asia.*.WARN)를 하나의 큐로 동적 바인딩(Binding)하는 필터링 룰을 해부할 수 있는가?
Industry
- 클라우드 인프라(AWS) 환경에서 SNS(Simple Notification Service)를 Pub/Sub의 익스체인지(Fanout)로 삼고, 그 뒤에 SQS(Simple Queue Service) 여러 개를 붙여 마이크로서비스 확장에 유연하게 대처하는 SNS-SQS Fan-out 파이프라인을 설계할 수 있는가?
- RabbitMQ에서 익스체인지에 연결된 큐가 단 한 개도 없을 때 발행된 메시지가 '블랙홀'처럼 조용히 증발(Drop)해버리는 현상을 방지하기 위해,
Alternate Exchange나Mandatory플래그를 설정하여 버려진 메시지를 회수하는 안전망을 논증할 수 있는가?