Phần 24 đã điểm qua rằng auto-configuration cho sẵn một RabbitAdmin, và nó tự khai báo mọi bean topology lúc khởi động. Bài này đo xem "lúc khởi động" thật ra là lúc nào — câu trả lời không như tên gọi.
Khai bằng bean
@Bean org.springframework.amqp.core.Queue viec() {
return QueueBuilder.durable("tp-viec").build();
}
@Bean TopicExchange suKien() { return new TopicExchange("tp-su-kien", true, false); }
@Bean Binding noi() { return BindingBuilder.bind(viec()).to(suKien()).with("don.#"); }
Nhiều thứ cùng lúc thì gom bằng Declarables cho đỡ rối:
@Bean Declarables nhom() {
return new Declarables(
QueueBuilder.durable("tp-a").build(),
QueueBuilder.durable("tp-b").build(),
new DirectExchange("tp-x", true, false));
}
Cả hai kiểu đều được khai lên broker:
hàng đợi tp-viec : đã khai
hàng đợi tp-a : đã khai
hàng đợi tp-b : đã khai
QueueBuilder mang theo mọi tham số của phần 6 — .autoDelete(), .exclusive(), .deadLetterExchange("dlx"), .ttl(1000). Đọc bean là biết hàng đợi thật trông thế nào.
Nhưng nó khai lúc nào
Đây là phần chính của bài. Tắt hẳn broker rồi khởi động ứng dụng:
ứng dụng khởi động: THÀNH CÔNG sau 0,4 giây (dù broker không có)
Hệ quả thực tế: deploy trúng lúc broker đang bảo trì thì mọi pod lên xanh, và sự cố chỉ lộ ra khi có thông điệp đầu tiên cần đi — có thể là nhiều giờ sau, ở một chỗ chẳng liên quan gì tới lần deploy đó.
Muốn thất bại sớm thì phải hỏi broker ngay lúc khởi động: bật management health indicator, hoặc gọi rabbitAdmin.getQueueInfo("hang-doi-quan-trong") trong một ApplicationRunner và ném ngoại lệ nếu nhận null.
Khai lệch tham số: 406, nhưng ứng dụng vẫn chạy
Hàng đợi tp-viec đã tồn tại với autoDelete=false. Khai lại với autoDelete=true:
ERROR o.s.a.r.c.CachingConnectionFactory : Shutdown Signal: channel error;
reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg 'auto_delete'
for queue 'tp-viec' in vhost '/': received 'true' but current is 'false'
ứng dụng vẫn đang chạy: true
khai một hàng đợi khác sau đó -> vẫn khai được
Đúng lỗi 406 mà phần 6 đã mổ, chỉ khác là ở đây nó chỉ là một dòng log ERROR. Ứng dụng lên bình thường, những khai báo khác vẫn chạy tiếp, và nếu bạn không đọc log lúc khởi động thì hàng đợi đó cứ giữ cấu hình cũ mãi mãi trong khi mã nguồn nói khác.
Đây là kiểu hỏng tệ nhất: mã và thực tế lệch nhau mà cả hai đều "hoạt động".
Khi nào để ứng dụng tự khai
Nên, khi ứng dụng là chủ sở hữu duy nhất của những hàng đợi đó — hàng đợi nội bộ, hàng đợi tạm, môi trường dev. Đặt topology cạnh mã dùng nó là cách tài liệu tốt nhất.
Không nên, khi hàng đợi được nhiều dịch vụ dùng chung. Phần 15 đã kết luận: một nơi duy nhất khai báo, các nơi còn lại chỉ kiểm tra. Với Spring, "chỉ kiểm tra" nghĩa là đừng khai bean Queue — chỉ trỏ @RabbitListener vào tên hàng đợi. Container sẽ thất bại rõ ràng nếu hàng đợi chưa có, thay vì âm thầm tạo một cái hơi khác.
Tuyệt đối không, với hàng đợi sản xuất có tham số quan trọng — DLX, TTL, quorum. Những thứ đó không sửa được tại chỗ: đổi ý là phải xoá hàng đợi và mất hết thông điệp đang nằm trong đó. Chúng thuộc về công cụ hạ tầng, nơi thay đổi được xem xét trước khi chạy.
Bài sau: xử lý lỗi trong listener — ErrorHandler, RepublishMessageRecoverer, và chỗ ngoại lệ thật sự đi tới.
Thử ba mươi giây
# so tham so that tren broker voi tham so trong ma nguon
docker exec -u rabbitmq rmq rabbitmqctl -q list_queues name durable auto_delete arguments
Nếu một dòng nào khác với QueueBuilder trong mã của bạn, nghĩa là đã có một dòng 406 trong log khởi động mà không ai đọc — và hàng đợi đang chạy theo cấu hình của lần đầu tiên nó được tạo ra.