Message Queues & Broker Physics
비동기 통신을 지원하는 메시지 큐의 원리와 메시지 브로커가 데이터를 일시 저장하고 대조하는 물리적 처리 메커니즘을 다루는 학습 노드입니다.
Article
M
Me
hyunyoun's Blog
network-communicationnetworkcommunicationdistributed-messagingevent-streamingmessage-queuesbroker-physicslearning11 min read
1. Overview
메시지 큐와 브로커 역학(Message Queues & Broker Physics)은 백엔드 서버 간의 동기(Synchronous) API 호출이 낳는 타임아웃과 강한 결합(Tight Coupling)의 사슬을 끊어버리고, 그사이에 '우체국(Broker)'을 두어 비동기(Asynchronous) 통신의 방파제를 세우는 인프라 설계의 기저를 해부합니다.
학습자는 A 서버가 B 서버를 직접 찌를 때 B 서버가 죽어 있으면 A 서버까지 같이 죽어버리는 연쇄 장애의 끔찍함을 깨닫고, 큐(Queue)라는 완충 지대를 통한 **시간적 디커플링(Temporal Decoupling)**의 원리를 뜯어봅니다. 나아가 HTTP의 무거움을 벗어던지고 메시징에 특화된 경량 이진 프로토콜인 AMQP와 MQTT의 철학을 장악합니다. 마지막으로, 생산자(Producer)가 큐에 넣는 속도와 소비자(Consumer)가 빼가는 속도가 다를 때 발생하는 메모리 백프레셔(Backpressure)와, 메시지를 무사히 받았는지 확인하는 네트워크 ACK (Acknowledgment) 역량을 확보합니다.
2. Scope & Boundaries
In-Scope
- Queue Data Structure: FIFO(First-In-First-Out)와 Point-to-Point 통신 패턴.
- Broker Protocols: AMQP (Advanced Message Queuing Protocol, RabbitMQ), MQTT (IoT 경량 프로토콜).
- Push vs Pull: 브로커가 컨슈머에게 밀어내는(Push) 방식과 컨슈머가 땡겨오는(Pull) 방식의 네트워크 제어권.
- Reliability & ACK: At-most-once, At-least-once, Exactly-once 의미론과 수신 확인(ACK) 메커니즘.
Out-of-Scope
- Event Sourcing & CQRS: 메시지를 기반으로 DB 상태를 분리하는 애플리케이션 아키텍처 패턴 07-03 Event-Driven Architecture 영역으로 위임.
- Log-structured Streaming (Kafka): 큐(Queue)를 넘어선 대용량 불변 로그 스트리밍 08-05-02 Event Streaming & Kafka Dynamics 영역으로 분리.
Boundaries
- HTTP API vs Message Broker: HTTP 통신은 '전화 통화'입니다. A가 전화를 걸었을 때 B가 안 받으면 통신은 실패합니다. 반면 메시지 브로커는 '이메일'입니다. A가 이메일을 큐에 던져놓으면, B가 컴퓨터가 꺼져있든 말든 상관없습니다. B가 내일 켜서 메일을 열어보면 됩니다. 이처럼 두 시스템이 **'동시에 살아있어야 할 필요성(시간적 결합)'**을 완전히 끊어내는 인프라적 패러다임 전환에 경계를 긋습니다.
3. Counterexample
- 동기 호출의 연쇄 붕괴 (Cascade Failure): 이벤트 날, '주문 서버'가 '결제 서버'로 직접 HTTP API를 찔렀습니다. 결제 서버가 트래픽 폭주로 10초 타임아웃이 나기 시작했습니다. 주문 서버의 스레드(Thread)들이 결제 서버의 응답을 기다리며 10초씩 물려있다가, 1분 만에 주문 서버의 스레드 풀(Thread Pool)이 모두 고갈되어 주문 서버마저 뻗어버렸습니다. 그 앞의 API Gateway마저 뻗으며 전체 시스템이 다운되었습니다. 사이에 메시지 큐를 넣어 "결제 요청 들어옴"이라는 쪽지만 던지고 주문 서버는 자기 할 일을 하러 가는 비동기 처리를 하지 않은 아키텍처 재앙입니다.
- ACK 없는 유실과 다중 결제: 서버가 큐에서 "100원 결제" 메시지를 꺼내서(Pop) DB에 저장하려는 찰나, 서버가 OOM(Out of Memory)으로 죽어버렸습니다. 큐는 이미 메시지를 꺼내줬으니 자기 메모리에서 삭제해버렸습니다. 이 결제는 영원히 증발했습니다. 이를 막고자 Auto-ACK를 끄고 "DB 처리 후 수동 ACK"로 바꿨는데, 이번엔 DB 저장은 성공해 놓고 ACK를 브로커에 쏴주기 0.1초 전에 서버가 죽었습니다. 브로커는 "처리 못 했네?" 하고 다른 서버에 똑같은 결제 메시지를 또 던져 100원이 두 번 결제되는 끔찍한 At-least-once 중복 처리 참사입니다.
4. Prerequisites
- L7 Protocol (Basic): HTTP의 Request-Response 한계. (08-04-01 HTTP Evolution)
- Data Structure (Basic): 큐(Queue) 자료구조의 FIFO 특성. (04-02-02 Queue & Stack)
5. Learning Map
6. Learning Topics
Basic
Core Topic 01: 시간의 족쇄 끊기 (Temporal Decoupling)
- Why to Learn: MSA(마이크로서비스) 환경에서 서버 수십 대가 HTTP API로 거미줄처럼 엮였을 때 발생하는 최악의 연쇄 장애(Cascade Failure)를 메시지 큐 1대로 우아하게 끊어내기 위함입니다.
- What to Learn:
- Concepts: Synchronous(동기) vs Asynchronous(비동기), Temporal Decoupling(시간적 결합 해제), Message Broker, Buffer.
- Skills: "회원 가입 완료 시 이메일 발송"이라는 비즈니스 로직에서, 이메일 서버가 3초 걸릴 때 회원가입 API 응답까지 3초 늦어지는 강결합 구조를 메시지 큐를 도입하여 0.1초 만에 응답하는 비동기 구조로 리팩터링.
- How to Learn:
- 1단계: 동기의 저주: A B C로 HTTP 요청이 이어집니다. C가 5초 걸리면 A도 5초를 대기해야 합니다. C가 죽으면 A도 에러를 뱉고 죽습니다.
- 2단계: 비동기의 방파제 (Buffer): A와 B 사이에 큐(Queue)를 둡니다. A는 "B야 이거 해라"라는 쪽지를 큐에 던지고(
0.01초 소요) 즉시 유저에게200 OK를 리턴합니다. B가 지금 죽어있어도 상관없습니다. 쪽지는 큐에 안전하게 쌓여있습니다(Buffer). B가 1시간 뒤에 재부팅되어 큐에서 쪽지를 꺼내 일을 처리하는, 완벽히 독립된 인프라 생태계를 뜯어봅니다.
- Implement: 동기 API vs 비동기 큐 벤치마크 렌더링.
Sync Mode:Client -> WebServer -> EmailServer(3초 지연). 웹 서버 스레드가 3초간 블록됨. 초당 100건 요청 시 스레드 풀 고갈로[503 Service Unavailable]폭발.Async Mode:Client -> WebServer -> RabbitMQ(0.01초). 웹 서버 스레드 즉시 반환. EmailServer가 죽어도 큐에 10,000건이 쌓일지언정 웹 서버는 평온하게200 OK를 내뱉는 무정지 차트 시각화.
Recommended
Core Topic 02: 경량화의 끝, AMQP와 MQTT (AMQP & MQTT Protocols)
- Why to Learn: 메시지를 주고받는 데 무겁고 오버헤드가 큰 HTTP 통신을 쓰는 짓을 멈추고, 목적에 맞게 극도로 다이어트된 이진(Binary) 프로토콜을 선택하는 엔지니어링 시야를 장악하기 위함입니다.
- What to Learn:
- Concepts: AMQP (Advanced Message Queuing Protocol), MQTT (Message Queuing Telemetry Transport), TCP Keep-alive, Binary Protocol.
- Skills: 엔터프라이즈 시스템 간의 복잡한 라우팅에는 AMQP를, 배터리가 10% 남은 스마트 농장의 온습도 센서에는 MQTT를 적용하는 프로토콜 트레이드오프 설계.
- How to Learn:
- 1단계: AMQP (신뢰와 복잡성): RabbitMQ의 심장입니다. 헤더, 바디, 라우팅 키(Routing Key), 익스체인지(Exchange) 등 복잡한 기업용 라우팅 룰을 다 담을 수 있는 규격화된 박스입니다. HTTP보다 가볍지만 꽤 무겁습니다.
- 2단계: MQTT (극강의 다이어트): IoT를 위해 만들어졌습니다. 헤더가 고작 2바이트(16비트)부터 시작합니다. 아프리카 사막의 센서가 불안정한 3G망으로 중앙 서버에 "온도 30도"라는 데이터를 쏠 때, 배터리와 네트워크 대역폭을 쥐어짜기 위해 군더더기를 다 날려버린 궁극의 경량 퍼블리싱 철학을 해부합니다.
- Implement: 프로토콜 페이로드(Payload) 바이트 비교 분석기.
"온도<25>25>" 데이터를 보낼 때.
HTTP/1.1:
POST /temp헤더 포함 약150 Bytes. AMQP:Basic.Publish프레임 약50 Bytes. MQTT:PUBLISH제어 패킷 약10 Bytes. 배터리로 구동되는 디바이스에서 하루 10,000번 전송 시 발생하는 배터리 소모량 차이 비교 시각화.
Practical
Core Topic 03: 밀어내기 vs 당겨오기 (Push vs Pull Dynamics)
- Why to Learn: 메시지 큐의 성능과 안정성은 '브로커가 컨슈머를 통제할 것인가(Push)', '컨슈머가 브로커를 통제할 것인가(Pull)'의 네트워크 주도권 싸움에 달려있음을 쥐기 위함입니다.
- What to Learn:
- Concepts: Push Model (RabbitMQ), Pull Model (Kafka), Backpressure (백프레셔), Consumer Prefetch.
- Skills: 컨슈머(서버)가 초당 10개밖에 처리 못 하는데 브로커가 초당 100개를 밀어 넣을 때(Push) 발생하는 OOM(Out of Memory) 현상과, 이를 제어하는 Prefetch 튜닝 기법.
- How to Learn:
- 1단계: Push 모델의 과부하 (RabbitMQ): 브로커는 똑똑합니다. 큐에 메시지가 들어오면 즉각 컨슈머에게 밀어 던집니다(Push). 속도는 가장 빠릅니다. 하지만 이벤트 날 트래픽이 폭주하여 초당 1만 개를 컨슈머에게 집어 던지면, 컨슈머의 RAM에 메시지가 쌓이다가 터져버립니다.
- 2단계: 백프레셔와 Pull (Kafka): 컨슈머가 터지는 걸 막으려면 방패(Backpressure)가 필요합니다. RabbitMQ에서는 컨슈머가 "나 지금 5개밖에 못 받으니까(Prefetch=5) 다 처리하면 더 줘!"라고 브로커의 Push 속도를 목 조릅니다. 반면 Kafka는 브로커가 멍청합니다. 가만히 있고, 컨슈머가 "나 여력 돼! 100개 줘!" 하고 스스로 땡겨가는(Pull) 방식을 취해 컨슈머 과부하를 원천 차단하는 네트워크 주도권 전환을 뜯어봅니다.
- Implement: Push 모델의 메모리 터짐(OOM) vs Prefetch 방어 시뮬레이터.
Broker: 초당 100개 생성.Consumer: 초당 10개 처리 가능.Prefetch=0 (무제한 Push): 컨슈머 메모리에 10초 만에 900개가 욱여넣어지며[CRASH] OOM Killed.Prefetch=10: 브로커가 10개만 주고 대기. 브로커의 큐에는 900개가 쌓여있음(브로커의 디스크가 방파제 역할). 컨슈머는 평온하게CPU 50%로 소화해 내는 트래픽 제어 렌더링.
Advanced
Core Topic 04: 절대 잃어버리지 않는 기술, 네트워크 ACK (The Physics of ACKs)
- Why to Learn: 메시지를 큐에서 꺼냈다고 완료된 게 아니라, '처리를 완료했다'는 확답(ACK)을 브로커에 쏘기 전까지는 언제든 서버가 죽어 메시지가 유실될 수 있는 분산 시스템의 가혹한 물리학을 장악하기 위함입니다.
- What to Learn:
- Concepts: Auto-ACK vs Manual-ACK, At-most-once, At-least-once, Exactly-once, Redelivery, Idempotency (멱등성).
- Skills: 결제 로직을 짤 때 서버가 중간에 죽어서 브로커가 똑같은 메시지를 다른 서버에 재전송(Redelivery)하더라도, 결제가 두 번 안 되도록 방어하는 멱등성 큐잉 설계.
- How to Learn:
- 1단계: Auto-ACK의 유실 (At-most-once): 브로커가 메시지를 줬을 때 묻지도 따지지도 않고 큐에서 지워버립니다. 최고로 빠릅니다. 근데 컨슈머 서버가 데이터를 까보기도 전에 죽어버리면 메시지는 영영 사라집니다(최대 1번 전달, 즉 0번일 수도 있음).
- 2단계: Manual-ACK의 중복 (At-least-once): 컨슈머가 "나 DB 저장까지 완벽히 끝냈어!"라고 명시적으로 ACK 패킷을 쏴줘야만 큐에서 지웁니다. 서버가 DB 저장 후 ACK를 쏘기 직전에 죽었습니다. 브로커는 "응답이 없네? 딴 서버에 다시 던져야지!" 하고 재전송(Redelivery)합니다. 데이터가 DB에 중복 저장됩니다(최소 1번 전달, 즉 2번일 수도 있음). 무조건 재전송될 수밖에 없는 이 환경에서 개발자가 스스로 '멱등성(Idempotency) 방어막'을 짜야만 하는 가혹한 현실을 해부합니다.
- Implement: 결제 서버 ACK 타이밍 딜레마 모사 (Python).
Msg: [결제 100원, ID: Tx99].Consumer_A수신 DB에 100원 차감 완료ACK전송 직전 프로세스Kill -9당함.Broker는 5초 타임아웃 후 큐에서Tx99복구Consumer_B에게 던짐(Redelivery). 만약Consumer_B가 멍청하면 100원을 또 깎음. 현명한Consumer_B는DB.find(Tx99)를 먼저 조회해 보고 "어? 이미 처리된 놈이네?" 하고 즉시ACK만 뱉고 넘겨버리는(멱등성 방어) 백엔드 생존 코드 렌더링.
7. Terminology
8. References
Primary
- [P1] CS2023 - Software Engineering (SE) - Distributed System Architectures (Asynchronous Messaging)
- [P5] SFIA - Systems Integration (SINT) - Messaging Protocols and Middleware
Secondary
- [Enterprise Integration Patterns] Gregor Hohpe - Point-to-Point Channel, Message Broker
- [RabbitMQ in Action] Alvaro Videla - AMQP Protocol and Message Acknowledgments
Industry
- [RabbitMQ Documentation] - Consumer Acknowledgements and Publisher Confirms
- [OASIS MQTT Standard] - MQTT Version 5.0 Specification
- [AWS Architecture Blog] - Using Amazon SQS for backpressure and throttling
9. Final Checklist
Primary
- 서버 A가 서버 B를 직접 HTTP로 호출할 때 B의 타임아웃 장애가 A까지 무너뜨리는 연쇄 장애(Cascade Failure)를, 중간에 Message Queue를 삽입하여 시간적 결합(Temporal Decoupling)을 해제함으로써 어떻게 방어하는지 설명할 수 있는가?
- 텍스트 기반의 무거운 HTTP/REST를 버리고, 엔터프라이즈 라우팅에 특화된 AMQP(RabbitMQ)와 헤더를 수 바이트 레벨로 줄여 배터리와 대역폭을 아끼는 MQTT(IoT) 프로토콜의 설계적 트레이드오프를 비교할 수 있는가?
Secondary
- 브로커가 컨슈머에게 메시지를 쏟아붓는 Push 모델(RabbitMQ)에서 컨슈머의 OOM(메모리 초과)을 막기 위한 Prefetch 제어와, 컨슈머가 자신의 처리 능력만큼 당겨오는 Pull 모델(Kafka)의 네트워크 통제권 차이를 논증할 수 있는가?
- 메시지가 큐에서 컨슈머로 전달되었으나 컨슈머가 로직 처리 전 죽었을 때 메시지가 영구 유실되는 Auto-ACK의 한계(At-most-once)를 지적할 수 있는가?
Industry
- 위 유실을 막기 위해 Manual-ACK(DB 트랜잭션 후 ACK 전송)를 도입했을 때 발생하는 부작용, 즉 'ACK 전송 직전의 컨슈머 다운'으로 인한 브로커의 무자비한 재전송(Redelivery) 딜레마를 식별할 수 있는가?
- 재전송으로 인해 동일한 결제 메시지가 2번 들어오는 At-least-once 환경에서, 메시지 고유 ID(Message ID)를 활용하여 DB에 두 번 인서트되지 않도록 방어하는 멱등성(Idempotency) 로직을 백엔드 코드 단에 설계할 수 있는가?