Bảy bài vừa rồi mổ từng kiểu exchange. Bài này ghép chúng lại thành một quyết định thật: hệ thống của bạn nên có một exchange hay nhiều, và bao nhiêu hàng đợi thì bắt đầu đau.
Ba cách bố trí
| Một exchange cho tất cả | Một exchange mỗi miền | Một exchange mỗi loại sự kiện | |
|---|---|---|---|
| Số exchange | 1 | ~10 | hàng trăm |
| Thêm bên nghe | một queueBind |
một queueBind |
một queueBind |
| Thêm loại sự kiện | không đụng gì | không đụng gì | sửa producer |
| Phân quyền ghi | tất cả hoặc không | theo miền | theo từng sự kiện |
| Không gian khoá | mọi đội dùng chung | mỗi miền một cõi | không cần khoá |
Cột giữa là mặc định tôi khuyên, và lý do chính không phải hiệu năng — mà là cột "phân quyền ghi".
Phần 12 đã đo: quyền trong RabbitMQ là biểu thức chính quy trên tên tài nguyên. Với một exchange duy nhất, mọi dịch vụ có quyền gửi đều gửi được mọi sự kiện, kể cả sự kiện của đội khác. Với một exchange mỗi miền, set_permissions viết được thành ^don-hang\. và ranh giới tổ chức trở thành ranh giới kỹ thuật. Tên exchange là đơn vị phân quyền — chọn nó trước khi nghĩ tới bất cứ thứ gì khác.
Trong mỗi miền thì dùng topic exchange, vì phần 10 đo được nó chỉ chậm hơn direct 10% mà đổi lại có gom nhóm theo tiền tố.
Cái giá đo được của topology lớn
Dựng broker sạch rồi khai N hàng đợi durable, mỗi cái một binding vào một topic exchange:
| 0 hàng đợi | 1 000 | 5 000 | |
|---|---|---|---|
| Thời gian khai báo | — | 3,0 s | 16,4 s |
| Bộ nhớ broker | 201 MiB | — | 520 MiB |
rabbitmqctl list_queues |
0,34 s | 0,36 s | 0,63 s |
GET /api/queues |
tức thì | — | 0,30 s, 3,8 MiB JSON |
| Khởi động lại tới khi nhận kết nối | 2,8 s | 2,6 s | 3,7 s |
Điều bảng đó nói
Khởi động lại rẻ hơn tôi tưởng. 5 000 hàng đợi durable chỉ thêm khoảng một giây so với broker rỗng. Tôi vào phép đo này với giả định "topology lớn làm restart lâu" và số liệu bác bỏ nó. Đây không phải lý do để giữ topology nhỏ.
Dựng mới mới là chỗ đau. 3,3 mili giây mỗi hàng đợi nghe không đáng gì, nhân với 5 000 thành 16 giây — và đó là thời gian khởi động của ứng dụng nếu bạn khai báo toàn bộ topology lúc bật. Với hàng chục instance cùng khởi động sau một lần deploy, con số đó nhân lên.
Bộ nhớ tuyến tính, khoảng 65 KB mỗi hàng đợi rỗng. 5 000 hàng đợi ăn 319 MiB trước khi có một thông điệp nào. Đây là con số để nhân lên khi ai đó đề xuất "mỗi người dùng một hàng đợi".
Công cụ vận hành chậm dần. 3,8 MiB JSON cho một lần mở trang quản trị là thứ bạn sẽ cảm thấy lúc đang xử lý sự cố. Đây là cái giá thật của topology phình to, và nó không hiện ra trong bất kỳ biểu đồ hiệu năng nào.
Số binding không phải vấn đề, số đích mới là
Gom các phép đo trước lại thành một luật:
| Đo ở | Kết quả |
|---|---|
| Phần 8: direct, 1 → 500 binding | phẳng (132k → 141k msg/s) |
| Phần 10: topic, 1 → 500 binding | gần phẳng (124k → 113k msg/s) |
| Phần 11: headers, 1 → 500 binding | sụp 93% (100k → 7k msg/s) |
| Phần 9: fanout, 1 → 100 đích | sụp 68 lần (150k → 2,2k msg/s) |
Hai dòng cuối hỏng vì hai lý do khác nhau: headers vì không có chỉ mục, fanout vì mỗi đích là một lần ghi thật. Hai dòng đầu cho thấy bạn có thể thoải mái thêm binding vào direct và topic.
Nên khi thiết kế, câu hỏi đúng là: mỗi thông điệp sẽ được ghi vào bao nhiêu hàng đợi? Con số đó nhân với lưu lượng ra tải thật của broker. Số exchange và số binding gần như không tính.
Quy ước đặt tên
<miền>.<phiên bản> exchange: don-hang.v1
<miền>.<thực thể>.<hành động> routing: don-hang.don.tao
<dịch vụ>.<mục đích> hàng đợi: ke-toan.ghi-so
Ba điều làm khổ người sau nếu bỏ qua:
Phiên bản trong tên exchange. Đổi schema sự kiện mà không có chỗ để chạy song song hai bản thì buộc phải deploy đồng thời mọi bên — và phần 6 cho thấy khai lại exchange với tham số khác là 406, tức không sửa tại chỗ được.
Tên hàng đợi mang tên dịch vụ sở hữu. Nhìn messages_unacknowledged tăng là biết ngay gọi ai. Hàng đợi tên emails thì không nói được gì.
Đừng nhét dữ liệu vào tên. don-hang.KH-8842 làm số hàng đợi phình theo số khách hàng — và bảng ở trên cho biết cái giá.
Ai khai báo topology
Một nơi duy nhất, không phải mọi dịch vụ. Phần 6 đo được rằng hai bên khai cùng một hàng đợi với tham số lệch nhau sẽ nhận 406 PRECONDITION_FAILED và channel đóng — lỗi nổ ra ở bên xui xẻo khởi động sau, chứ không phải bên khai sai.
Cách chắc chắn: một script hoặc một dịch vụ dựng topology; các dịch vụ khác chỉ dùng queueDeclarePassive để kiểm tra sự tồn tại. Chúng sẽ thất bại rõ ràng khi topology chưa sẵn sàng, thay vì âm thầm tạo ra một hàng đợi hơi khác.
Bài sau: acknowledgement — vì sao autoAck=true là chấp nhận mất thông điệp, và mất bao nhiêu.
Thử ba mươi giây
# hang doi nao dang ton bo nho nhat, va co ai doc chung khong
docker exec -u rabbitmq rmq rabbitmqctl -q list_queues name messages consumers memory \
| sort -k4 -rn | head
Dòng nào có consumers=0 mà memory lớn là hàng đợi đã bị bỏ quên — thứ chiếm phần lớn trong 319 MiB của bảng trên ở một hệ thống thật.