Exchange là trái tim định tuyến của RabbitMQ. Producer luôn publish vào exchange chứ không vào queue. Cách exchange phân phối message phụ thuộc vào loại của nó. Bài này đi qua 4 loại exchange, cách binding hoạt động, và pattern định tuyến với topic.
| Loại | Cách định tuyến | Ví dụ routing key |
|---|---|---|
| direct | Routing key của message khớp chính xác với binding key của queue | payment.success |
| fanout | Broadcast tới mọi queue đã bind, bỏ qua routing key | (không dùng) |
| topic | Routing key khớp theo pattern dùng ký tự đại diện * và # | order.*, logs.# |
| headers | Khớp theo các header của message thay vì routing key | (dựa trên headers) |
Direct exchange chuyển message tới các queue có binding key trùng khít với routing key của message. Đây là loại đơn giản và phổ biến nhất cho định tuyến 1-1 hoặc phân loại theo nhãn cố định.
Ví dụ: queue q.error bind với key error, queue q.info bind với key info. Message gửi với routing key error chỉ vào q.error. Nếu nhiều queue cùng bind key error thì cả hai đều nhận (multicast theo nhãn).
Fanout sao chép mỗi message tới tất cả queue đang bind vào nó, hoàn toàn bỏ qua routing key. Đây là kiểu publish/subscribe kinh điển: một sự kiện được phát ra, mọi subscriber đều nhận bản sao.
Ví dụ: một event "user đăng ký" được fanout tới queue gửi email chào mừng, queue cập nhật thống kê, và queue đồng bộ CRM — cả ba cùng nhận, mỗi bên xử lý theo việc của mình.
Topic là loại mạnh và linh hoạt nhất. Routing key được viết thành các "từ" ngăn cách bởi dấu chấm (vd order.created.vn). Binding key dùng ký tự đại diện:
| Ký tự | Ý nghĩa |
|---|---|
* | Khớp đúng một từ |
# | Khớp không hoặc nhiều từ |
Ví dụ binding key order.* khớp order.created và order.paid nhưng không khớp order.created.vn (vì * chỉ một từ). Binding key order.# khớp tất cả: order.created, order.created.vn, kể cả order. Pattern logs.error.* khớp logs.error.db, logs.error.api.
Khai báo topic exchange và bind queue với pika (Python):
import pika conn = pika.BlockingConnection(pika.ConnectionParameters("localhost")) ch = conn.channel() # 1. khai báo topic exchange (durable để sống qua restart broker) ch.exchange_declare(exchange="logs_topic", exchange_type="topic", durable=True) # 2. tạo queue và bind theo pattern ch.queue_declare(queue="q.order", durable=True) ch.queue_bind(queue="q.order", exchange="logs_topic", routing_key="order.*") ch.queue_declare(queue="q.error", durable=True) ch.queue_bind(queue="q.error", exchange="logs_topic", routing_key="logs.error.*") # 3. publish — broker so khớp routing key với các binding ch.basic_publish(exchange="logs_topic", routing_key="order.created", body="new order") ch.basic_publish(exchange="logs_topic", routing_key="logs.error.db", body="db down") conn.close()
# khớp mọi thứ). Vì vậy nhiều hệ thống mặc định dùng topic để linh hoạt về sau.Headers exchange bỏ qua routing key và định tuyến dựa trên các cặp header của message. Khi bind, ta khai báo các header cần khớp cùng tham số x-match:
| x-match | Điều kiện |
|---|---|
all | Phải khớp tất cả header đã khai (giống AND) |
any | Chỉ cần khớp một header bất kỳ (giống OR) |
Ví dụ bind với {"x-match": "all", "format": "pdf", "type": "report"} chỉ nhận message có đồng thời header format=pdf và type=report. Headers exchange ít dùng hơn nhưng hữu ích khi tiêu chí định tuyến phức tạp và không tiện nhét vào một chuỗi routing key.
Routing key là nhãn do producer gắn vào message lúc publish. Binding key là nhãn khai báo lúc bind queue vào exchange. Broker so khớp routing key (của message) với binding key (của các binding) theo luật của loại exchange để quyết định queue đích.
Default exchange là một direct exchange không tên (chuỗi rỗng "") có sẵn. Mọi queue tự động được bind vào nó với binding key chính là tên queue. Nhờ vậy bạn có thể publish "thẳng" vào một queue bằng cách đặt routing key = tên queue:
# gửi thẳng vào queue tên "task_queue" qua default exchange ch.basic_publish(exchange="", routing_key="task_queue", body="hello")