Mở bất kỳ hướng dẫn RabbitMQ nào, dòng đầu tiên sau khi kết nối đều là thế này:
ch.queueDeclare("hello", false, false, false, null);
Chạy nó trên RabbitMQ 4.3.5 (bản mà phần 2 dựng lên):
541 INTERNAL_ERROR - Feature `transient_nonexcl_queues` is deprecated.
By default, this feature is not permitted anymore.
The feature will be removed from a future major RabbitMQ version...
Và không chỉ lệnh đó hỏng — cả connection bị đóng. Bài này đi qua bốn cờ khai báo, tám tổ hợp của chúng, và bốn mã lỗi mà mỗi cái có phạm vi sát thương khác nhau.
Bốn tham số
ch.queueDeclare(name, durable, exclusive, autoDelete, arguments);
| Cờ | Nghĩa | Sống sót qua |
|---|---|---|
durable |
Định nghĩa hàng đợi được ghi xuống đĩa | Broker khởi động lại |
exclusive |
Chỉ connection khai báo được dùng | Không: chết cùng connection |
autoDelete |
Tự xoá khi consumer cuối rời đi | Không: chết khi hết consumer |
arguments |
TTL, độ dài tối đa, kiểu hàng đợi, DLX… | — |
Chú ý cột cuối: durable nói về định nghĩa hàng đợi, không nói gì về thông điệp bên trong. Phần sau của sê-ri sẽ đo chuyện đó.
Tám tổ hợp, hai cái không còn được phép
Thử cả tám trên broker sạch:
| durable | exclusive | auto-delete | Kết quả |
|---|---|---|---|
| true | true | true | cho phép |
| true | true | false | cho phép |
| true | false | true | cho phép |
| true | false | false | cho phép |
| false | true | true | cho phép |
| false | true | false | cho phép |
| false | false | true | 541, đóng connection |
| false | false | false | 541, đóng connection |
Luật rút gọn: hàng đợi không bền mà lại dùng chung cho nhiều connection thì không còn được phép. Không bền thì phải là của riêng một connection.
Lý do RabbitMQ chặn: một hàng đợi transient dùng chung là thứ không ai gọi lại được sau khi broker khởi động lại, nhưng lại vẫn nhận thông điệp như thường trong lúc chạy — nguồn mất dữ liệu âm thầm. Chặn ở tầng khai báo là cách duy nhất bắt được nó.
Sửa thì đơn giản: đổi false đầu tiên thành true. Hàng đợi bền không tốn gì thêm cho tới khi có thông điệp trong đó.
Bốn mã lỗi, hai phạm vi sát thương
Đây là chỗ đáng nhớ hơn cả danh sách cờ:
| Mã | Khi nào | Giết cái gì |
|---|---|---|
404 NOT_FOUND |
queueDeclarePassive hàng đợi không có |
channel |
405 RESOURCE_LOCKED |
Chạm vào hàng đợi exclusive của connection khác | channel |
406 PRECONDITION_FAILED |
Khai lại với tham số khác lần trước | channel |
541 INTERNAL_ERROR |
Tổ hợp cờ bị cấm | cả connection |
AlreadyClosedException. Nhiều mã nguồn bắt ngoại lệ của queueDeclare, ghi log rồi đi tiếp trên cùng channel — và mọi thứ sau đó hỏng vì một lý do chẳng liên quan gì.
406 PRECONDITION_FAILED: khai lại khác tham số
khai lần 1 (durable=true) : OK
khai lần 2 (auto-delete=true): 406 PRECONDITION_FAILED - inequivalent arg 'auto_delete'
for queue 'co-thu': received 'true' but current is 'false'
channel sau đó : ĐÃ ĐÓNG
Thông báo này rất tử tế — nó nói rõ cờ nào lệch, nhận gì và đang là gì. Đây là lỗi hay gặp nhất khi hai dịch vụ cùng khai một hàng đợi mà chỉ một bên được sửa. Cách tránh: một nơi duy nhất khai báo topology, các nơi còn lại dùng queueDeclarePassive — chỉ hỏi, không tạo.
exclusive: riêng tư và chết cùng connection
chủ sở hữu tự hỏi : rieng-tu
connection khác chạm vào : 405 RESOURCE_LOCKED - cannot obtain exclusive access
sau khi chủ đóng kết nối : KHÔNG CÒN (dù khai durable=true)
Dòng cuối là chỗ dễ hiểu nhầm nhất: exclusive thắng durable. Khai bền đến mấy thì hàng đợi exclusive vẫn biến mất khi connection tạo ra nó đóng lại. Đó là ý đồ thiết kế — nó dành cho hàng đợi tạm của một client, kiểu hàng đợi trả lời trong mẫu RPC.
autoDelete: chỉ tính từ consumer đầu tiên
khai xong, CHƯA có consumer: CÒN <- vẫn còn
đang có 1 consumer : CÒN
consumer cuối rời đi : KHÔNG CÒN
Hàng đợi autoDelete không tự xoá lúc vừa khai. Nó chờ có ít nhất một consumer rồi mới bắt đầu đếm; consumer cuối cùng rời đi thì nó biến mất. Nếu producer khai hàng đợi rồi gửi trước khi consumer kịp kết nối, thông điệp vẫn nằm đó chờ — và đó là hành vi đúng.
Nên khai thế nào
Mặc định: durable=true, hai cờ kia false. Đây là hàng đợi nghiệp vụ bình thường, và giờ cũng là tổ hợp duy nhất không có bẫy.
exclusive=true cho hàng đợi tạm của một client — hàng đợi trả lời, hàng đợi theo dõi. Để tên rỗng cho broker tự đặt.
autoDelete=true chỉ khi hàng đợi thật sự vô nghĩa lúc không ai nghe — kiểu phát tán sự kiện thời gian thực. Với hàng đợi công việc thì đây là cách mất việc.
Bài sau: một thông điệp gồm những gì, và vì sao deliveryMode=2 đo ra nhanh hơn deliveryMode=1.
Thử ba mươi giây
# xem cờ thật của mọi hàng đợi đang có
docker exec -u rabbitmq rmq rabbitmqctl -q list_queues \
name durable auto_delete exclusive messages
Nếu có hàng đợi nghiệp vụ nào hiện durable=false, bạn vừa tìm ra thứ sẽ biến mất trong lần bảo trì tiếp theo.