Bốn kiểu exchange của RabbitMQ khác nhau ở đúng một chỗ: cách so khoá. Bài này mổ kiểu đơn giản nhất — direct, so khoá bằng phép bằng chính xác — và đo hai thứ hay bị đồn đoán về nó.
Một khoá, một đích
ch.queueBind("kho", "don-hang", "moi");
ch.queueBind("ke-toan", "don-hang", "thanh-toan");
ch.queueBind("van-chuyen", "don-hang", "giao");
gửi 3 thông điệp, 3 khoá khác nhau -> kho=1 ke-toan=1 van-chuyen=1
Đây là toàn bộ ý tưởng của direct exchange: routing key của thông điệp phải bằng đúng khoá của binding. Không có mẫu, không có ký tự đại diện, không phân biệt hoa thường theo kiểu "thông minh".
Chép chứ không chia
Hai hàng đợi cùng bind một khoá, gửi một thông điệp:
gửi 1 thông điệp khoá 'moi' -> kho=1 ke-toan=1
Một thông điệp vào, hai bản ra. Phần 3 đã chạm vào chuyện này và nó đáng nhắc lại vì đây là gốc của hiểu nhầm phổ biến nhất về RabbitMQ: exchange chép thông điệp sang mọi đích khớp, nó không chia việc. Muốn chia việc thì cho nhiều consumer vào cùng một hàng đợi — bài sau đo cả hai kiểu cạnh nhau.
Một hàng đợi nghe nhiều khoá
for (String k : new String[]{"moi", "thanh-toan", "giao"})
ch.queueBind("tong-hop", "don-hang", k);
bind 3 khoá, gửi 4 thông điệp -> tong-hop=3
Cái thứ tư mang khoá khac và không đi đâu cả. Một hàng đợi có bao nhiêu binding tuỳ ý; đây là cách dựng "hàng đợi tổng hợp" nghe vài loại sự kiện mà không cần đổi bên gửi.
Khớp đúng từng ký tự
Gửi năm biến thể của cùng một chữ vào một binding khoá moi:
gửi 5 biến thể: "moi", "Moi", "MOI", "moi ", " moi"
nhận được: 1
Chỉ đúng một cái tới đích. Bốn cái còn lại — khác hoa thường, thừa một dấu cách — biến mất không để lại gì. Không lỗi, không log, không bộ đếm nào nhúc nhích.
Cách bắt tận tay: bật mandatory và gắn ReturnListener như phần 4 đã đo — broker sẽ trả về 312 NO_ROUTE kèm chính cái routing key đã gửi.
Khoá rỗng vẫn là một khoá
bind khoá "" rồi gửi khoá "" -> nhận được 1
Chuỗi rỗng không phải "không có khoá", nó là một khoá bình thường có độ dài 0. Hữu ích khi bạn muốn một direct exchange hành xử như fanout cho đúng một hàng đợi — nhưng nếu đó là ý định thì hãy dùng fanout cho người sau đọc hiểu.
Default exchange: dùng được, bind thì không
ch.queueBind("thu-bind", "", "khoa-tu-dat");
-> channel 403 ACCESS_REFUSED - operation not permitted on the default exchange
Default exchange là một direct exchange, nhưng broker khoá nó lại: bạn không thêm binding vào đó được. Mọi binding của nó do broker tự tạo, mỗi hàng đợi một cái, khoá trùng tên hàng đợi — phần 5 đã thấy chúng trong list_bindings.
Nghĩa là với default exchange, routing key bắt buộc là tên hàng đợi. Không có cách nào khác.
500 binding không làm chậm gì cả
Câu hỏi tự nhiên: exchange có nhiều binding thì định tuyến có chậm đi không? Đo 20 000 thông điệp có publisher confirms, hâm nóng trước, trung vị 3 lượt:
| Số hàng đợi gắn vào exchange | Thông lượng |
|---|---|
| 1 | 132 701 msg/s |
| 50 | 136 230 msg/s |
| 500 | 140 709 msg/s |
Số binding tăng 500 lần, thông lượng không đổi — chênh lệch 6% nằm trong nhiễu và thậm chí nghiêng về phía nhiều binding hơn.
Lý do: direct exchange không duyệt danh sách binding. Nó tra bảng băm theo routing key, nên chi phí không phụ thuộc vào tổng số binding mà chỉ phụ thuộc vào số đích khớp. Ở phép đo trên, mỗi thông điệp luôn chỉ tới đúng một hàng đợi.
Đó cũng là lời cảnh báo cho bài sau: chi phí thật không nằm ở kiểu exchange hay số binding, mà ở số hàng đợi mà thông điệp thật sự rơi vào. Fanout sẽ cho thấy điều đó rất rõ.
Khi nào direct là đúng
Khi bạn phân loại theo một chiều duy nhất và rời rạc: loại sự kiện, mức độ ưu tiên, mã vùng. Danh sách khoá hữu hạn, ai cũng đọc hiểu, và thêm một loại mới chỉ tốn một queueBind.
Khi cần lọc theo nhiều chiều — don-hang.vn.gap — thì đó là việc của topic exchange, phần 10.
Thử ba mươi giây
# xem toan bo ban do dinh tuyen cua broker trong mot lenh
docker exec -u rabbitmq rmq rabbitmqctl -q list_bindings \
source_name source_kind destination_name routing_key
Dòng nào có source_name rỗng là binding ngầm của default exchange. Còn nếu bạn đang tìm một thông điệp thất lạc, hãy so cột routing_key ở đây với chuỗi bên gửi thật sự truyền vào — chúng khác nhau nhiều hơn bạn tưởng.