Bài 2

Exchanges & Routing

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.

4 loại exchange
direct · fanout · topic · headers
LoạiCách định tuyếnVí dụ routing key
directRouting key của message khớp chính xác với binding key của queuepayment.success
fanoutBroadcast tới mọi queue đã bind, bỏ qua routing key(không dùng)
topicRouting key khớp theo pattern dùng ký tự đại diện *#order.*, logs.#
headersKhớp theo các header của message thay vì routing key(dựa trên headers)
Direct exchange
Khớp routing key chính xác

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 exchange
Broadcast tới mọi queue

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 exchange
Khớp pattern với * và #

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.createdorder.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()
Mẹo: topic exchange có thể "biến thành" direct (binding key không có ký tự đại diện) hoặc fanout (binding key # 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
Khớp theo header, không theo routing key

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
allPhải khớp tất cả header đã khai (giống AND)
anyChỉ 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=pdftype=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.

Binding key vs Routing key & Default exchange
Hai khái niệm dễ lẫn

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")
Lưu ý: đây chỉ là "đường tắt" tiện cho ví dụ đơn giản. Trong hệ thống thật nên khai báo exchange tường minh để kiểm soát định tuyến rõ ràng.
← Bài trước
Bài 1: Khái niệm cốt lõi