RabbitMQ ์ต์ 3.13/4.0+ ๊ธฐ์ค ์ด๋ณด์ ์๋ฒฝ ๊ฐ์ด๋: Docker 1์ด ์ค์น, AMQP ํต์ฌ ๋ฉํ ๋ชจ๋ธ, 4๋ Exchange(Direct/Fanout/Topic/Headers), Work Queue & Prefetch ๊ณต์ ๋ถ๋ฐฐ, ์๋ ACK, ๋ฉ์์ง ์์์ฑ, DLX(๋ฐ๋ ๋ ํฐ ํ), Spring Boot ์ฐ๋ ๋ฐ rabbitmqctl ๋ช
๋ น์ด ์นํธ์ํธ
## 1. ํ์ ์ฌ์ ์ค์น ์๊ฑด & ํ๊ฒฝ ๊ตฌ์ฑ (Prerequisites)
RabbitMQ๋ ๊ฐ๋ณ๊ณ ์ ๋ขฐ์ฑ์ด ๋ฐ์ด๋ ์คํ์์ค AMQP ๋ฉ์์ง ๋ธ๋ก์ปค์
๋๋ค. ์ด๋ณด์๋ 1๋ถ ์์ ์ง์ ์ค์ตํด ๋ณผ ์ ์๋๋ก ๊ฐ์ฅ ๋น ๋ฅด๊ณ ๊ถ์ฅ๋๋ Docker ์คํ๋ถํฐ ์์ํฉ๋๋ค.
### ๐ณ 1. Docker๋ก 1์ด ๋ง์ ์คํํ๊ธฐ (๊ฐ์ฅ ์ถ์ฒ)
RabbitMQ ์น ๊ด๋ฆฌ์ UI(Management Plugin)๊ฐ ํฌํจ๋ ๊ณต์ ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํ๋ฉด ๋ณ๋์ Erlang ์ค์น ์์ด ์ฆ์ ์คํํ ์ ์์ต๋๋ค.
```bash
# RabbitMQ ๋ฐ ์น ๊ด๋ฆฌ ์ฝ์ ์ปจํ
์ด๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ์คํ
docker run -d --name rabbitmq \
-p 5672:5672 \
-p 15672:15672 \
-e RABBITMQ_DEFAULT_USER=guest \
-e RABBITMQ_DEFAULT_PASS=guest \
rabbitmq:3-management
```
- **5672 ํฌํธ**: AMQP ํด๋ผ์ด์ธํธ ์ ํ๋ฆฌ์ผ์ด์
ํต์ ํฌํธ
- **15672 ํฌํธ**: ๋ธ๋ผ์ฐ์ ์น ๊ด๋ฆฌ ๋์๋ณด๋ ํฌํธ
- **์ ์ ํ์ธ**: ์น ๋ธ๋ผ์ฐ์ ์์ `http://localhost:15672` ์ ์ โ ์์ด๋/๋น๋ฐ๋ฒํธ `guest` / `guest` ์
๋ ฅ ํ ๋ก๊ทธ์ธ
---
### ๐ป 2. ๋ก์ปฌ OS ์ง์ ์ค์น (Docker ๋ฏธ์ฌ์ฉ ์)
RabbitMQ๋ Erlang ๊ธฐ๋ฐ์ด๋ฏ๋ก ํธํ๋๋ **Erlang OTP 26 ์ด์**์ด ๋จผ์ ์ค์น๋์ด ์์ด์ผ ํฉ๋๋ค.
| ์ด์์ฒด์ (OS) | ์ค์น ๋ช
๋ น์ด / ๋ฐฉ๋ฒ |
| :--- | :--- |
| **Windows** | 1. [Erlang ๋ค์ด๋ก๋](https://www.erlang.org/patches/otp-26.2) ์ค์น ํ ํ๊ฒฝ๋ณ์ ๋ฑ๋ก<br>2. [RabbitMQ ์ธ์คํจ๋ฌ](https://github.com/rabbitmq/rabbitmq-server/releases) ์คํ (๋๋ `choco install rabbitmq`) |
| **macOS** | ํฐ๋ฏธ๋์์ `brew install rabbitmq` ์คํ<br>์คํ: `brew services start rabbitmq` |
| **Linux (Ubuntu/Debian)** | `sudo apt update && sudo apt install -y rabbitmq-server`<br>์๋น์ค ๊ธฐ๋: `sudo systemctl enable --now rabbitmq-server` |
์ค์น ํ ์น ๊ด๋ฆฌ์ ํ๋ฌ๊ทธ์ธ ํ์ฑํ:
```bash
rabbitmq-plugins enable rabbitmq_management
```
---
### ๐ฆ 3. ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น
์ค์ตํ ์ธ์ด์ RabbitMQ ๋๋ผ์ด๋ฒ ํจํค์ง๋ฅผ ์ค์นํฉ๋๋ค:
```bash
# Python (ํ์ค pika ๋ผ์ด๋ธ๋ฌ๋ฆฌ)
pip install pika
# Node.js
npm install amqplib
# Java / Spring Boot (Gradle)
# implementation 'org.springframework.boot:spring-boot-starter-amqp'
```
---
## 2. RabbitMQ ํต์ฌ ๊ฐ๋
& ๋ฉํ ๋ชจ๋ธ (Mental Model)
AMQP(Advanced Message Queuing Protocol)์ ํต์ฌ์ **"์์ฐ์(Producer)๋ ํ์ ์ง์ ๋ฉ์์ง๋ฅผ ๋ฃ์ง ์๊ณ , ๋ฐ๋์ ์ต์ค์ฒด์ธ์ง(Exchange)๋ฅผ ๊ฑฐ์น๋ค"**๋ ์ ์
๋๋ค.
```text
[AMQP ๋ฉ์์ง ์ ๋ฌ ํ์ดํ๋ผ์ธ]
โโโโโโโโโโโโโ Routing Key โโโโโโโโโโโโ Binding Key โโโโโโโโโโโโโ
โ Producer โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโบโ Exchange โโโโโโโโโโโโโโโโโโบโ Queue โ
โ (์์ฐ์) โ "์ฃผ๋ฌธ ์๋ฃ ์ด๋ฒคํธ ์ ์ก" โ (์ฐ์ฒด๊ตญ) โ "๋ผ์ฐํ
๊ท์น" โ (์ฐํธํจ) โ
โโโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโฌโโโโโโ
โ
Consumeโ
โผ
โโโโโโโโโโโโโ
โ Consumer โ
โ (์๋น์) โ
โโโโโโโโโโโโโ
```
### ๐ 4๋ ํต์ฌ ๊ตฌ์ฑ์์
1. **Producer (์์ฐ์)**: ๋ฉ์์ง๋ฅผ ์์ฑํ์ฌ Exchange์ ๋ฐํํ๋ ์ฃผ์ฒด.
2. **Exchange (์ต์ค์ฒด์ธ์ง)**: ๋ฉ์์ง๋ฅผ ๋ฐ์ ๋ฐ์ธ๋ฉ ๊ท์น(Binding Key)์ ๋ฐ๋ผ ์ ์ ํ Queue๋ก ๋ผ์ฐํ
ํ๋ ์ฐ์ฒด๊ตญ.
3. **Queue (ํ)**: ๋ฉ์์ง๊ฐ ์๋น์์๊ฒ ์๋น๋ ๋๊น์ง ์์ ํ๊ฒ ๋๊ธฐํ๋ ๋ฉ๋ชจ๋ฆฌ/๋์คํฌ ๋ฒํผ.
4. **Consumer (์๋น์)**: ํ์์ ๋๊ธฐ ์ค์ธ ๋ฉ์์ง๋ฅผ ๊ฐ์ ธ์ ์ฒ๋ฆฌํ๋ ์ ํ๋ฆฌ์ผ์ด์
.
### ๐ 4๋ Exchange ํ์
๋น๊ต
| Exchange ํ์
| ๋ผ์ฐํ
๋์ ์๋ฆฌ | ์ฃผ์ ์ค๋ฌด ํ์ฉ ์์ |
| :--- | :--- | :--- |
| **Direct** | ๋ฉ์์ง์ `routing_key`์ ํ์ `binding_key`๊ฐ **์ ํํ ์ผ์น**ํ ๋ ์ ๋ฌ | 1:1 ์์
ํ, ํน์ ๋จ์ผ ์๋น์ค ๋์ ๋ช
๋ น ์ ๋ฌ |
| **Fanout** | ๋ผ์ฐํ
ํค๋ฅผ ๋ฌด์ํ๊ณ **๋ฐ์ธ๋ฉ๋ ๋ชจ๋ ํ๋ก ๋ฌด์กฐ๊ฑด ๋ณต์ ๋ธ๋ก๋์บ์คํธ** | ์ค์๊ฐ ์๋ฆผ ์ ์ฒด ๊ณต์ง, ์ฌ๋ฌ ๋ง์ดํฌ๋ก์๋น์ค ๋์ ์์ |
| **Topic** | ๋ผ์ฐํ
ํค์ **์์ผ๋์นด๋ ํจํด(`*`, `#`)**์ ๋งค์นญํ์ฌ ์ ์ฐํ๊ฒ ์ ๋ฌ | ๋ค์ค ์์คํ
๋ก๊ทธ ์์ง (`app.error`, `order.#`) |
| **Headers** | ๋ผ์ฐํ
ํค ๋์ **๋ฉ์์ง ํค๋์ Key-Value ์์ฑ**์ ๋น๊ตํ์ฌ ๋ผ์ฐํ
| ๋ณตํฉ ๋ฉํ๋ฐ์ดํฐ ์กฐ๊ฑด๋ถ ๋ผ์ฐํ
|
---
## 3. [์ด๋ณด์ 1๋จ๊ณ] ํฌ๋ก์๋: ๊ฐ์ฅ ๋จ์ํ 1:1 ๋ฉ์์ง ์ก์์
๊ธฐ๋ณธ(Default) Direct Exchange๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ก ๋ฉ์์ง๋ฅผ ๋ณด๋ด๊ณ ๋ฐ๋ ์ต์ ๋์ ์์ ์
๋๋ค.
### ๐ค ์์ฐ์: `producer.py`
```python
import pika
# 1. RabbitMQ ์๋ฒ ์ฐ๊ฒฐ (๊ธฐ๋ณธ ํฌํธ 5672)
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
# 2. ๋ฉ์์ง๋ฅผ ๋ฐ์ ํ ์ ์ธ (ํ๊ฐ ์์ผ๋ฉด ์๋ ์์ฑ)
channel.queue_declare(queue='hello_queue')
# 3. ๋ฉ์์ง ๋ฐํ (๊ธฐ๋ณธ ์ต์ค์ฒด์ธ์ง ์ฌ์ฉ: routing_key๊ฐ ํ ์ด๋ฆ์ด ๋จ)
message = '๋ฐ๊ฐ์ต๋๋ค! RabbitMQ ์ฒซ ๋ฉ์์ง์
๋๋ค.'
channel.basic_publish(
exchange='',
routing_key='hello_queue',
body=message.encode('utf-8')
)
print(f" [V] ์ ์ก ์๋ฃ: '{message}'")
# 4. ์ฐ๊ฒฐ ์ข
๋ฃ
connection.close()
```
### ๐ฅ ์๋น์: `consumer.py`
```python
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='hello_queue')
# ๋ฉ์์ง ์์ ์ ํธ์ถ๋ ์ฝ๋ฐฑ ํจ์
def callback(ch, method, properties, body):
print(f" [OK] ์์ ๋ ๋ฉ์์ง: {body.decode('utf-8')}")
# ๊ตฌ๋
๋ฑ๋ก (auto_ack=True: ๋ฉ์์ง๋ฅผ ๋ฐ์๋ง์ ํ์์ ์ฆ์ ์๋ ์ญ์ )
channel.basic_consume(
queue='hello_queue',
on_message_callback=callback,
auto_ack=True
)
print(' [*] ๋ฉ์์ง ๋๊ธฐ ์ค์
๋๋ค. ์ข
๋ฃํ๋ ค๋ฉด CTRL+C๋ฅผ ๋๋ฅด์ธ์.')
channel.start_consuming()
```
---
## 4. [์ด๋ณด์ 2๋จ๊ณ] ์์
ํ (Work Queues) & ๊ณต์ ํ ๋ถ๋ฐฐ (Prefetch)
์ฌ๋ฌ ์์ปค(Worker) ํ๋ก์ธ์ค๊ฐ ํ๋์ ํ๋ฅผ ๋๋์ด ์ฒ๋ฆฌํ ๋, ์์
๋์ ๋ฐ๋ผ ๊ณต์ ํ๊ฒ ๋ฐฐ๋ถํ๋ ํจํด์
๋๋ค.
```text
[Fair Dispatch: ๊ณต์ ํ ์ผ๊ฐ ๋ถ๋ฐฐ]
โโโโโโโโโโโโโโโโโ โโโบ [Worker 1] (๋๋ฆฐ ์์
์ฒ๋ฆฌ ์ค...)
[Producer] โโโบ [Queue]โ โ๏ธ โ๏ธ โ๏ธ โ๏ธ โ๏ธ โ
โโโโโโโโโโโโโโโโโ โโโบ [Worker 2] (๋น ๋ฅธ ์์
์ฒ๋ฆฌ ์๋ฃ โ ๋ค์ ๋ฉ์์ง ์ฆ์ ์์ )
(prefetch_count=1: ํ ๋ฒ์ 1๊ฐ์ฉ๋ง ์ ๋ฌ)
```
### โก ํต์ฌ ์ค์ 2๊ฐ์ง
1. **์๋ ํ์ธ ์๋ต (Manual ACK)**: ์์
๋์ค ํ๋ก์ธ์ค๊ฐ ๋น์ ์ ์ข
๋ฃ๋๋ฉด ๋ฉ์์ง๊ฐ ๋ค๋ฅธ ์์ปค์๊ฒ ์ฌํ ๋น๋๋๋ก `basic_ack()`๋ฅผ ์๋ ํธ์ถํฉ๋๋ค.
2. **๊ณต์ ๋ถ๋ฐฐ (`basic_qos(prefetch_count=1)`)**: ์์ปค๊ฐ ํ์ฌ ์์
์ ์๋ฃํ๊ณ ACK๋ฅผ ๋ณด๋ด๊ธฐ ์ ๊น์ง๋ ์ ๋ฉ์์ง๋ฅผ ๋ณด๋ด์ง ์์ต๋๋ค.
```python
import pika
import time
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
# ํ ๋ด๊ตฌ์ฑ(durable=True): RabbitMQ๊ฐ ์ฌ์์๋์ด๋ ํ ์ ์ง
channel.queue_declare(queue='task_queue', durable=True)
# ๐ ํต์ฌ: ๊ณต์ ๋ฐฐ๋ถ (์์ปค๋น ์ฒ๋ฆฌ ์ค์ธ ๋ฏธ์น์ธ ๋ฉ์์ง ์ต๋ 1๊ฐ๋ก ์ ํ)
channel.basic_qos(prefetch_count=1)
def do_work(ch, method, properties, body):
msg = body.decode('utf-8')
print(f" [x] ์์
์์: {msg}")
# ์์
์๋ฎฌ๋ ์ด์
time.sleep(2)
print(f" [x] ์์
์๋ฃ: {msg}")
# ๐ ํต์ฌ: ์์
์ฑ๊ณต ์๋ฃ ํ ๋ธ๋ก์ปค์ ์๋ ACK ์ ์ก
ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_consume(queue='task_queue', on_message_callback=do_work, auto_ack=False)
channel.start_consuming()
```
---
## 5. [์ด๋ณด์ 3๋จ๊ณ] ๋ฐํ/๊ตฌ๋
(Publish/Subscribe - Fanout Exchange)
๋์ผํ ์ด๋ฒคํธ๋ฅผ ์ฌ๋ฌ ์๋น์ค(์: ์ด๋ฉ์ผ ๋ฐ์ก ํ, ๊ฐ์ฌ ๋ก๊ทธ ํ, ํต๊ณ ํ)๋ก ๋์์ ๋ณต์ ๋ฐฐํฌํ๋ ํจํด์
๋๋ค.
```text
[Fanout ๋ธ๋ก๋์บ์คํธ]
โโโโโโโโโโโโโโโโโโโโ โโโบ [Email Queue] โโโบ [Email Service]
[Producer] โโโบ [Fanout Exchange] โ
โโโโโโโโโโโโโโโโโโโโ โโโบ [Log Queue] โโโบ [Log Service]
(๋ชจ๋ ๋ฐ์ธ๋ฉ ํ๋ก ๋ฌด์กฐ๊ฑด ๋ณต์ฌ)
```
```python
# 1. Fanout ์ต์ค์ฒด์ธ์ง ์ ์ธ
channel.exchange_declare(exchange='logs_fanout', exchange_type='fanout')
# 2. ์์ ์ต๋ช
ํ ์์ฑ (exclusive=True: ์๋น์ ์ข
๋ฃ ์ ํ ์๋ ์ญ์ )
result = channel.queue_declare(queue='', exclusive=True)
queue_name = result.method.queue
# 3. ํ๋ฅผ Fanout ์ต์ค์ฒด์ธ์ง์ ๋ฐ์ธ๋ฉ
channel.queue_bind(exchange='logs_fanout', queue=queue_name)
# 4. ๋ฐํ ์์๋ routing_key๋ฅผ ๋น์๋์ด๋ ๋ชจ๋ ํ๋ก ์ ๋ฌ๋จ
channel.basic_publish(
exchange='logs_fanout',
routing_key='',
body=b'System Alert: Database backup completed!'
)
```
---
## 6. [์ด๋ณด์ 4๋จ๊ณ] ํ ํฝ ๊ธฐ๋ฐ ๋ผ์ฐํ
(Topic Exchange)
๋ฉ์์ง ๋ผ์ฐํ
ํค์ ์ (`.`)์ผ๋ก ๊ตฌ๋ถ๋ ๋จ์ด๋ฅผ ์กฐํฉํ๊ณ , ์์ผ๋์นด๋๋ก ์ํ๋ ํจํด๋ง ์ ํ ๊ตฌ๋
ํฉ๋๋ค.
### ๐ฏ ์์ผ๋์นด๋ ๊ท์น
- `*` (๋ณํ): **์ ํํ 1๊ฐ์ ๋จ์ด** ๋งค์นญ (์: `kern.*` โ `kern.info`, `kern.crit`)
- `#` (์ฐ๋ฌผ์ ): **0๊ฐ ์ด์์ ๋จ์ด** ๋งค์นญ (์: `audit.#` โ `audit`, `audit.user.login`)
```text
[Topic ๋ผ์ฐํ
๋งค์นญ ์์]
Routing Key: "auth.user.login_failed"
โโโ Binding: "*.user.*" โ ๋งค์นญ ์ฑ๊ณต! โ
โโโ Binding: "auth.#" โ ๋งค์นญ ์ฑ๊ณต! โ
โโโ Binding: "payment.*" โ ๋งค์นญ ์คํจ โ
```
```python
# Topic ์ต์ค์ฒด์ธ์ง ์์ฑ
channel.exchange_declare(exchange='topic_logs', exchange_type='topic')
# ์ค์ ์๋ฌ ํ: ๋ชจ๋ ์์คํ
์ critical ๋ก๊ทธ๋ง ์์ (*.critical)
channel.queue_bind(exchange='topic_logs', queue='critical_queue', routing_key='*.critical')
# ๋ณด์ ๊ฐ์ฌ ํ: ๋ณด์ ๋ชจ๋์ ๋ชจ๋ ํ์ ์ด๋ฒคํธ ์์ (security.#)
channel.queue_bind(exchange='topic_logs', queue='security_queue', routing_key='security.#')
# ๋ฉ์์ง ๋ฐํ ์์
channel.basic_publish(
exchange='topic_logs',
routing_key='security.user.password_reset',
body=b'User 42 reset password'
)
```
---
## 7. ๋ฉ์์ง ์ ๋ขฐ์ฑ & ์์์ฑ ๋ณด์ฅ (Persistence)
์๋ฒ ์ ์์ด ๊บผ์ง๊ฑฐ๋ ์ฌ๋ถํ
๋์ด๋ ๋ฉ์์ง๊ฐ ์ฆ๋ฐํ์ง ์๋๋ก ๋ณด์ฅํ๋ 3๋จ๊ณ ์์ ์ฅ์น์
๋๋ค.
```python
# 1. ํ ์์ํ (durable=True)
channel.queue_declare(queue='secure_orders', durable=True)
# 2. ๋ฉ์์ง ๋์คํฌ ์์ํ (delivery_mode=2)
channel.basic_publish(
exchange='',
routing_key='secure_orders',
body=b'Order #987654',
properties=pika.BasicProperties(
delivery_mode=pika.DeliveryMode.Persistent # ๋์คํฌ ์ ์ฅ
)
)
# 3. ํผ๋ธ๋ฆฌ์
ํ์ธ ๋ชจ๋ (Publisher Confirms): ๋ธ๋ก์ปค๊ฐ ๋์คํฌ ๊ธฐ๋ก ํ ACK ๋ฐํ ๋ณด์ฅ
channel.confirm_delivery()
```
---
## 8. ๋ฐ๋ ๋ ํฐ ํ (Dead Letter Exchange - DLX)
๋ฉ์์ง ์ฒ๋ฆฌ์ ์คํจํ๊ฑฐ๋(Rejected), ์ง์ ๋ ์ ํจ์๊ฐ(TTL)์ด ์ด๊ณผ๋ ๋ฉ์์ง๋ฅผ ์ ์ค ์์ด ๋ณ๋ ๊ฒฉ๋ฆฌ ํ๋ก ์ด๋์์ผ ์ฌ์๋ํ๋ ํจํด์
๋๋ค.
```python
# 1. ์คํจ ๋ฉ์์ง๋ฅผ ๋ณด๊ดํ DLX ๋ฐ ๋ฐ๋ ๋ ํฐ ํ ์ค๋น
channel.exchange_declare(exchange='dlx_exchange', exchange_type='direct')
channel.queue_declare(queue='dead_letter_queue', durable=True)
channel.queue_bind(exchange='dlx_exchange', queue='dead_letter_queue', routing_key='retry_failed')
# 2. ๋ฉ์ธ ์
๋ฌด ํ ์ ์ธ ์ DLX ์ธ์ ์ฐ๊ฒฐ
args = {
'x-dead-letter-exchange': 'dlx_exchange',
'x-dead-letter-routing-key': 'retry_failed',
'x-message-ttl': 60000 # 60์ด ๋์ ์๋น๋์ง ์์ผ๋ฉด DLX๋ก ์ด๋
}
channel.queue_declare(queue='main_work_queue', durable=True, arguments=args)
```
---
## 9. ์ค๋ฌด ๋ฐฑ์๋ ์ฐ๋: Spring Boot (Spring AMQP)
Spring Boot ํ๊ฒฝ์์ ๊ฐ์ฅ ๋ง์ด ์ฐ์ด๋ ์ค์ ๋ฐ ๋ฆฌ์ค๋ ์ด๋
ธํ
์ด์
ํจํด์
๋๋ค.
### ๐ `application.yml`
```yaml
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
listener:
simple:
acknowledge-mode: manual # ์๋ ACK ๋ชจ๋
prefetch: 1 # ๊ณต์ ๋ถ๋ฐฐ
```
### โ `@RabbitListener` ์์ ๋ฐ ๋ฐํ ์ฝ๋
```java
@Service
public class OrderMessageService {
@Autowired
private RabbitTemplate rabbitTemplate;
// ๋ฉ์์ง ๋ฐํ (Direct Exchange)
public void sendOrder(String orderId) {
rabbitTemplate.convertAndSend("order.exchange", "order.created", orderId);
}
// ๋ฉ์์ง ์์ ๋ฐ ์๋ ACK ์ฒ๋ฆฌ
@RabbitListener(queues = "order.queue")
public void receiveOrder(String orderId, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
try {
System.out.println("์ฃผ๋ฌธ ์ฒ๋ฆฌ ์งํ: " + orderId);
// ๋น์ฆ๋์ค ๋ก์ง ์ํ...
channel.basicAck(tag, false); // ์ ์ ์ฒ๋ฆฌ ์น์ธ
} catch (Exception e) {
// ์คํจ ์ ํ ์ฌ์ง์
์ฌ๋ถ(requeue=false) ์ง์ ํ์ฌ DLX๋ก ์ด๊ด
channel.basicNack(tag, false, false);
}
}
}
```
---
## 10. CLI ํต์ฌ ๊ด๋ฆฌ ๋ช
๋ น์ด ์นํธ์ํธ (`rabbitmqctl`)
```bash
# 1. ์๋ฒ ์ํ ๋ฐ ๋
ธ๋ ํฌ์ค์ฒดํฌ
rabbitmqctl status
rabbitmqctl ping
# 2. ํ ๋ฐ ์ต์ค์ฒด์ธ์ง ์ํ ์กฐํ
rabbitmqctl list_queues name messages_ready messages_unacknowledged consumers
rabbitmqctl list_exchanges name type
rabbitmqctl list_bindings
# 3. ์ฌ์ฉ์ ๊ณ์ ๋ฐ ๊ถํ ๊ด๋ฆฌ
rabbitmqctl add_user admin SuperSecretPass123!
rabbitmqctl set_user_tags admin administrator
rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
# 4. ๊ฐ์ ํธ์คํธ(vhost) ๊ด๋ฆฌ
rabbitmqctl add_vhost my_vhost
rabbitmqctl list_vhosts
# 5. ํ ๊ฐ์ ๋น์ฐ๊ธฐ (์ฃผ์)
rabbitmqctl purge_queue hello_queue
```
---
## 11. ์ค๋ฌด ์ฃผ์์ฌํญ & ํ๋ก๋์
์ฒดํฌ๋ฆฌ์คํธ
| ์ ๊ฒ ํญ๋ชฉ | ๊ถ์ฅ ์ฌํญ | ์ด์ |
| :--- | :--- | :--- |
| **Auto-ACK ์ฌ์ฉ ๊ธ์ง** | ์ค๋ฌด์์๋ ๋ฐ๋์ ์๋ ACK(`auto_ack=False`) ์ ์ฉ | ์ ํ๋ฆฌ์ผ์ด์
์ฅ์ ์ ์ฒ๋ฆฌ ์ค์ด๋ ๋ฉ์์ง ์ ์ค ๋ฐฉ์ง |
| **Prefetch ์ ํ ์ค์ ** | `prefetch_count=1 ~ 50` ์ง์ | ํน์ ์์ปค์๊ฒ ๋ฉ์์ง๊ฐ ์์ฒ ๊ฐ ์ ๋ฆฌ๋ ๋ณ๋ชฉ ํ์ ์ฐจ๋จ |
| **์์์ฑ ํ๋๊ทธ ๋๊ธฐํ** | `durable=True` ๋ฐ `delivery_mode=2` ํจ๊ป ์ ์ฉ | ๋ ์ค ํ๋๋ผ๋ ๋๋ฝ๋๋ฉด ์๋ฒ ์ฌ์์ ์ ๋ฐ์ดํฐ ์์ค |
| **Unacked ๋์ ๊ฐ์** | ์น ์ฝ์์์ Unacked ์์น๊ฐ ๊ณ์ ์ฆ๊ฐํ๋์ง ๋ชจ๋ํฐ๋ง | `basic_ack` ํธ์ถ ๋๋ฝ ์ ๋ฉ๋ชจ๋ฆฌ ๊ณ ๊ฐ ์์ธ 1์์ |
| **Quorum Queue ๋์
** | ๊ณ ๊ฐ์ฉ์ฑ ํด๋ฌ์คํฐ ๊ตฌ์ถ ์ Classic ํ ๋์ Raft ๊ธฐ๋ฐ Quorum ํ ์ฌ์ฉ | ๋ฐ์ดํฐ ์ ํฉ์ฑ ๋ณด์ฅ ๋ฐ ๋ธ๋ก์ปค ์ฅ์ ์ ๋ฌด์ค๋จ ๋ณต๊ตฌ |
์๊ฒฌ ๋ฐ ์ง๋ฌธ
0์์ง ๋ฑ๋ก๋ ์๊ฒฌ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋๊ธ์ ๋จ๊ฒจ๋ณด์ธ์!
๋๊ธ ์์
๋๊ธ ์ญ์