Direct exchange so khoá bằng phép bằng. Topic exchange so theo mẫu, và hai ký tự đại diện của nó khác nhau ở một chỗ mà tài liệu hay nói lướt qua. Bài này dựng bảng khớp đầy đủ bằng cách gửi thật.
Khoá là một chuỗi các từ ngăn bởi dấu chấm
don.vn.gap
│ │ └── mức độ
│ └───── vùng
└───────── loại
Hai ký tự đại diện:
| Ký tự | Khớp với |
|---|---|
* |
đúng một từ |
# |
không hoặc nhiều từ |
Chữ "không" trong dòng thứ hai là toàn bộ nội dung của bài này.
Bảng khớp, đo bằng cách gửi thật
Chín mẫu binding nhân bảy routing key, mỗi ô là một lần gửi và một lần đếm:
mẫu binding don don.vn don.vn.gap don.us.thuong gap don.vn.gap.them (rỗng)
────────────────────────────────────────────────────────────────────────────────────
don.vn.gap · · ✓ · · · ·
don.*.gap · · ✓ · · · ·
don.# ✓ ✓ ✓ ✓ · ✓ ·
*.vn.* · · ✓ · · · ·
# ✓ ✓ ✓ ✓ ✓ ✓ ✓
* ✓ · · · ✓ · ·
#.gap · · ✓ · ✓ · ·
don.* · ✓ · · · · ·
*.*.* · · ✓ ✓ · · ·
Ba điều bảng đó nói
don.# khớp cả khoá don trần. Không có từ nào sau don. mà vẫn khớp, vì # chấp nhận không từ nào. Đây là hành vi bị hiểu nhầm nhiều nhất — nhiều người đọc don.# là "don chấm gì đó" và ngạc nhiên khi hàng đợi nhận cả sự kiện don không có hậu tố.
Cùng lý do đó, #.gap khớp cả gap trần lẫn don.vn.gap.
* không khớp khoá rỗng, # thì có. Dòng * và dòng # ở cột cuối khác nhau. Nếu bạn muốn một hàng đợi hứng mọi thứ để kiểm toán, chỉ có # làm được — * bỏ sót cả khoá rỗng lẫn mọi khoá nhiều từ.
Số dấu chấm là ràng buộc cứng. *.*.* chỉ khớp khoá đúng ba từ; don.* chỉ khớp khoá đúng hai từ bắt đầu bằng don, nên nó không khớp don.vn.gap. Người ta hay viết don.* với ý "mọi sự kiện đơn hàng" rồi mất hết sự kiện ba tầng. Ý đó viết là don.#.
* là đúng một từ, # là không hoặc nhiều từ. Gần như mọi lỗi định tuyến topic đều là dùng * ở chỗ đáng lẽ phải là #.
Dấu chấm là dấu chấm, không gì khác
mẫu "a.b" gặp khoá "a.b" -> KHỚP
mẫu "a.b" gặp khoá "a-b" -> không
mẫu "a.b" gặp khoá "a..b" -> không
mẫu "a.b" gặp khoá "a.b." -> không
mẫu "a.b" gặp khoá "A.b" -> không
Chỉ dấu chấm mới tách từ. Dấu chấm thừa ở cuối tạo ra một từ rỗng và làm hỏng phép khớp, y như phần 8 đã thấy với direct — phân biệt hoa thường, và khoảng trắng thừa cũng tính.
Topic gần như miễn phí
So ba kiểu exchange trên cùng một phép đo — 20 000 thông điệp, 50 hàng đợi gắn vào, mỗi thông điệp khớp đúng một đích, có publisher confirms, hâm nóng trước, trung vị 3 lượt:
| Kiểu | Thông lượng | So với direct |
|---|---|---|
| direct | 120 725 msg/s | 100% |
| topic | 108 049 msg/s | 90% |
| headers | 50 454 msg/s | 42% |
Topic chỉ chậm hơn direct 10% dù phải so mẫu thay vì tra bảng băm — RabbitMQ dựng cây tiền tố cho các binding topic nên chi phí không tỉ lệ với số binding. Dòng cuối là chuyện của bài sau.
Nói cách khác: đừng chọn direct thay topic vì lý do hiệu năng. Chọn direct khi phân loại thật sự chỉ có một chiều, chọn topic khi có nhiều chiều — và nhớ rằng chuyển từ direct sang topic sau này là đổi cả bên gửi lẫn mọi binding, việc không hề rẻ.
Thiết kế khoá
Đi từ chung nhất tới riêng nhất, vì mẫu chỉ cắt được từ trái sang:
<miền>.<thực thể>.<hành động>.<biến thể> don-hang.don.tao.vn
Với thứ tự đó, don-hang.# lấy cả miền, don-hang.don.# lấy cả thực thể, *.*.tao.* lấy một hành động ở mọi miền. Đặt ngược lại — biến thể trước — thì không mẫu nào gom nhóm được.
Và đừng nhét dữ liệu vào khoá. don.vn.gap là phân loại; don.vn.gap.KH-8842 là mã khách hàng lọt vào chỗ không thuộc về nó, làm số binding phình theo dữ liệu.
Thử ba mươi giây
Thử một mẫu mà không cần viết consumer — HTTP API trả thẳng routed: true/false:
A="-su guest:guest"; H='Content-Type: application/json'
curl $A -X PUT localhost:15672/api/queues/%2F/thu-mau -H "$H" -d '{"durable":true}'
curl $A -X PUT localhost:15672/api/exchanges/%2F/thu -H "$H" -d '{"type":"topic","durable":true}'
curl $A -X POST localhost:15672/api/bindings/%2F/e/thu/q/thu-mau -H "$H" -d '{"routing_key":"don.#"}'
for k in don don.vn don.vn.gap gap; do
echo -n "$k -> "
curl $A -X POST localhost:15672/api/exchanges/%2F/thu/publish -H "$H" \
-d "{\"properties\":{},\"routing_key\":\"$k\",\"payload\":\"x\",\"payload_encoding\":\"string\"}"
done
don -> {"routed":true}
don.vn -> {"routed":true}
don.vn.gap -> {"routed":true}
gap -> {"routed":false}
Dòng đầu là bằng chứng cho tiêu đề bài này. Dùng cách đó thử mẫu của bạn trước khi cắm nó vào hệ thống thật — rẻ hơn nhiều so với đi tìm thông điệp thất lạc.