자주 사용하는 미들웨어의 통신 방법 1 : 메시지 브로커와 큐 본문
미들웨어
미들웨어는 애플리케이션 사이에서 데이터 흐름을 중계하거나 처리하기 위한 소프트웨어를 말한다. 사실 단순히 큐나 메시지 브로커만을 말하는 줄 알았는데, 정의에 따르면 WAS, GW, MQ, DBMS 등 내가 기존에 생각하고 있던 것보다 더 넓은 범위의 키워드로 생각된다.
요즘 이벤트 서버 성능 개선에 머리를 쓰고 있고, 서버를 구성하는 각 요소들의 성능 개선을 위해선 지금 어떻게 통신하고 있는지부터 알아야겠다는 생각이었다. 자주 사용하고 있는 미들웨어들의 기본 설정이나 통신 방식들을 정리한다.
# 메시지 브로커와 큐
1. Kafka
2. RabbitMQ
3. Redis pub/sub
4. AWS SQS
5. Mosquitto와 MQTT
# 웹 서버
6. Tomcat
7. Nginx
8. ALB
# DBMS
9. Mysql
10. MongoDB
Kafka
1. 프로토콜과 연결 유지
TCP 기반의 바이너리 프로토콜로 통신하기에 최초 연결 시 핸드셰이크가 필요하다.
Connection pooling으로 연결을 재사용하고, 클라이언트의 호출이 없거나 하트 비트가 일정 시간 동안 없으면 연결을 종료한다.
2. Batch 기반의 Produce
Kafka는 높은 처리량을 위해 메시지 배치 전송한다.
배치 주기와 크기를 기반으로 전송 단위를 설정할 수 있다.
그 주기와 크기가 너무 작으면 처리량이 낮아질 것이고, 반대로 너무 크다면 지연율이 높아지게 된다.
3. Producer 설정
기본 Producer 설정은 다음과 같다.
사이즈가 다 차지 않아도 대기 시간이 만족하면 배치가 실행되고, 대기 시간이 만족하지 않아도 크기가 넘어서면 배치가 실행된다.
- batch size : 메시지 크기가 모였을 때 전송할 크기를 말한다. (16KB)
- linger.ms : 배치를 처리하기 전 대기할 시간을 의미한다. (5ms)
The default changed from 0 to 5 in Apache Kafka 4.0 as the efficiency gains from larger batches typically result in similar or lower producer latency despite the increased linger.
4. Long polling 기반의 Consumer
카프카는 Long polling으로 메시지를 수신한다.
기준 시간 동안 대기하고, 대기 중 메시지 도착 시 또는 시간 초과 시 반환하길 반복한다.
이때도 하나의 메시지만 수신하는 것이 아닌, 배치로 여러 메시지를 한 번에 가져와서 하나씩 처리한다.
처리 후에는 Commit 처리로 컨슈머 그룹 별로 저장되어 있는 Offset 위치를 변경한다.
아래는 카프카 4.0의 기본 설정이다.
- max.poll.interval.ms : Consumer가 poll() 호출 사이에 허용되는 최대 간격 (그 간격 안에 poll 없을 시 리밸런싱)
- max.poll.records : 한 번의 poll() 가져오는 최대 레코드 수 (전체 파티션 범위)
- enable.auto.commit : 자동 오프셋 커밋 사용 여부
max.poll.interval.ms = 300000 (5분)
max.poll.records = 500
enable.auto.commit = true
* 처리량과 지연율에 대한 비유
처리량과 지연률을 화물 트럭으로 비유하자.
- 트럭에 적재되는 시간을 기다려 많은 짐을 실을 수록, 더 적은 운송 횟수로 짐을 옮길 수 있다.
- 반대로 짐이 적재된 시간과 도착한 시간의 간격이 길어진다.
- 전체 짐의 관점으로 보면 더 빠르게 전달되었지만, 각 짐을 사용하지 못하는 시간은 길어졌다.
처리량은 적은 횟수의 네트워크 처리로 더 많은 메시지가 전달되길 말하고,
지연율은 각 메시지가 전달을 시작한 시점부터, 목적지에 도달되는 시간을 말한다.
RabbitMQ
1. Push 기반의 낮은 지연성
Consumer와 Queue가 연결을 유지하고 있다가, 큐에 메시지 도착 즉시 전달받는다.
브로커에서 Consumer에게 Push 하는 방식으로 메시지가 전달되고, 따라서 낮은 지연성을 갖는다.
Producer가 Exchange에 메시지를 발행하면, Exchange에서는 바인딩 규칙에 따라 전체 혹은 일부 큐에 메시지가 전달된다.
Exchange의 라우팅 방식
- fanout : 브로드 캐스트
- direct : routing key에 맞는 큐로 전달
- topic : routing key의 패턴에 맞는 큐로 전달
- header : 메시지 헤더에 맞는 큐로 전달
2. 신뢰성
컨슈머에게 메시지가 전달되면, 브로커에서 해당 메시지는 UnAcked 상태로 해당 순서에 보관된다.
컨슈머는 처리 후 그 결과를 Broker에 ACK/NACK로 알린다.
만약 컨슈머가 처리 중 연결이 끊기거나 죽는 상황을 방지하기 위해 메시지는 TTL을 갖는다.
NACK, Reject, Timeout 이 발생하면 브로커에선 해당 메시지를 Requeue 상태로 돌려 컨슘이 가능하도록 한다.
DLQ 나 재시도 예외 처리로, 컨슈머가 무의미하게 특정 메시지를 반복 컨슘 - 재시도하는 것을 막는다.
3. 완벽하지 않은 순서 보장
메시지를 순서대로 수신하지만, 이전 메시지가 "처리 완료" 되어야 다음 메시지가 수신될 수 있는 구조는 아니다.
한 큐에 하나의 컨슈머만을 강제하지 않기에, 한 메시지가 앞선 메시지보다 먼저 처리될 수 있다.
또, Prefetch에 의해 한 컨슈머 안에서도 여러 메시지가 병렬 처리되는 과정에서 순서가 보장되지 않을 수 있다.
이를 해결하기 위해선 Prefetch를 1로 하고, 큐 당 한 컨슈머를 강제해야 하나, 이때의 낮아진 처리량에 주의한다.
4. Prefetch
한 컨슈머가 동시에 수신할 수 있는 메시지 수를 지정하여 처리량을 조정한다.
Redis Pub / Sub
1. RESP
Redis pub / sub은 "REdis Serialization Protol" 기반의 단순 텍스트 프로토콜로 통신한다.
TCP 위에서 동작하기에 핸드셰이크를 맺고, Keep-Alive로 연결을 유지한다.
2. 휘발성, 비신뢰성
수신자의 연결 상태와 상관없이 메시지를 발행한다.
수신자가 메시지를 정상적으로 수신/처리되었는지 확인하지 않는다. (Fire and forget)
메시지 히스토리를 기억하거나 큐잉을 하지 않고, Push 기반의 전달이 이뤄진다.
3. 채널 기반 라우팅, 브로드 캐스팅
채널 이름 또는 패턴 기반의 구독이 가능하다.
메시지가 Publish 되면 채널을 구독하는 모든 수신 측에 메시지가 전달된다.
AWS SQS
1. Pull 방식의 컨슘
컨슈머가 브로커에 질의하는 방식으로 메시지를 수신한다.
Long polling과 Short polling 모두를 지원한다.
* Long polling vs Short polling
Short polling : 요청한 시점에 현재 상태를 응답 받음
Long polling : 응답 대기 시간 동안 변경 상황을 응답 받음
서버 부하나 실시간성에는 Long polling이 유리하나,
연결이 많아져 서버 리소스 부족으로 이어질 수 있고 구현 난이도가 높음
2. Batch 처리
송수신 모두 최대 10개의 메시지를 한 번에 발행 / 수신할 수 있다.
3. Visibility timeout
메시지를 수신하면 브로커에서 해당 메시지를 지우는 것이 아닌, 컨슘 불가능 상태로 표시한다.
컨슈머에서 처리를 마치면, 처리한 메시지를 지우는 요청을 전달한다.
제거되지 않은 메시지는 가시성 타임아웃 시간 이후에 메시지가 컨슘 가능 상태로 전환된다.
이 가시성 타임아웃 규칙을 이용하여 처리 실패된 메시지를 재시도 처리한다.
DLQ 구성과 메시지 보관 기간을 조절하여 재시도 처리의 반복을 제한한다.
4. 높은 처리량을 지향하는 Standard 큐
Standard queue는 메시지 순서를 보장하지 않는다.
처리량과 낮은 지연율 지향하고, 메시지 중복 처리 문제가 발생할 수 있다.
5. 신뢰성을 지향하는 FIFO 큐
메시지 Key를 기준으로 한 순서 보장이 가능하다.
동일 키끼리의 메시지는 순서가 보장되기에, HOL 블록킹 문제, 처리 지연 문제가 발생할 수 있다.
중복 방지 (동일 메시지 발행 제한)으로 큐 안에 동일한 메시지가 발행되는 문제를 피할 수 있다.
단, 메시지가 처리되는 도중 가시성 타임 아웃이 끊기면 해당 메시지를 다시 수신하게 되니 컨슈머 측에서의 중복 처리에 유의한다.
Mosquitto (MQTT)
1. 연결 유지, Push 방식
클라이언트와 브로커가 연결을 유지하고, 메시지가 발생하면 브로커에서 Push 한다.
2. 신뢰성
수신과 발행 측 중 더 낮은 QoS 설정을 따른다.
기본 설정은 QoS 0이다.
3. QoS
QoS 0 : 수신이나 처리 결과를 보장하지 않는다. 즉시 전달 후 메시지 손실 (fire and forget)
QoS 1 : 최소 한 번의 수신은 보장하지만, 중복 처리의 위험이 있다.
- 수신자가 메시지를 정상적으로 받으면 PUBACK을 날려 송신자에게 알린다.
- 지연이나 패킷 손실로 PUBACK을 못 받으면, 미리 설정된 주기 이후에 재전송한다.
QoS 2 : 최소 한번 보장, 중복을 막는다.
- 수신자가 메시지를 정상적으로 받으면 PUBACK을 날려 송신자에게 알린다. 이는 수신은 했지만, 처리는 아직을 의미한다.
- 송신자가 최종 처리를 시작할 것을 주문한다. (PUBREL)
- 수신자가 최종 처리를 완료하고 이를 알린다. (PUBCOMP)
4. 메시지 보관
Mosquitto는 메시지 보관이 필요할 때도 있지만, 영구 저장소의 목적은 아니다.
Retain 메시지 : 센서값, 설정 정보 등 새로운 Subscriber에 최신 상태를 전달하기 위한 보관 메시지
Offline 메시지 : QoS가 1 또는 2일 때, 구독자가 오프라인이 되는 경우를 대비한 보관, 재접속 시 전달
'Architecture > Infrastructure' 카테고리의 다른 글
| 자주 사용하는 미들웨어의 통신 방법 2 : 웹 서버와 DBMS (2) | 2025.08.24 |
|---|---|
| IaC 처리 : 팀에서 테라폼을 도입하고 얻은 것들 (0) | 2025.01.01 |
| Vault k8s injector 소개 : 쿠버네티스에서 Vault를 다루는 방법 (0) | 2024.05.31 |
| 쿠버네티스 배포 : 롤링 업데이트 동작 흐름과 무중단 처리 (0) | 2024.05.31 |
| CDN URL 암호화 : CloudFront signed url (1) | 2024.04.10 |