Ba mươi tám bài, mỗi bài một phép đo. Bài này gom lại thành thứ dùng được: bảng những gì đáng sửa, xếp theo mức tác động — và bảng những gì đừng mất thời gian.
Phép đo tổng kết
Hai cách viết cùng một việc: gửi và tiêu thụ 2 000 thông điệp persistent, cùng một mức đảm bảo (có publisher confirms, ack thủ công, không mất thông điệp nào).
cách tệ : connection mới cho mỗi thông điệp, confirm từng cái, prefetch 1, ack từng cái
cách tốt : dùng lại channel, confirm theo lô, prefetch 100, ack theo lô
| Thời gian | |
|---|---|
| Cách tệ | 6,28 s |
| Cách tốt | 0,05 s |
| 122 lần |
Không có tham số broker nào được đổi. Không có phần cứng nào khác. Toàn bộ chênh lệch nằm ở bốn quyết định trong mã ứng dụng — và cả bốn đều là mặc định hoặc thói quen mà người mới hay chọn.
Thứ thật sự đổi được hiệu năng
Xếp theo mức tác động đo được:
| Thay đổi | Tác động | Đo ở |
|---|---|---|
| Dùng lại channel thay vì mở connection mỗi thông điệp | 1 128× | Phần 3 |
| Bỏ retry chặn luồng, chuyển sang hàng đợi chờ | ~1 000× thời gian mẻ | Phần 29 |
| Confirm bất đồng bộ thay vì confirm từng thông điệp | 68× | Phần 17 |
| Giảm số hàng đợi đích của fanout (100 → 1) | 68× | Phần 9 |
| Prefetch 1 → 100 (với ack thủ công) | 19× | Phần 19 |
| Dùng topic thay headers khi có nhiều binding | 15× ở 500 binding | Phần 11 |
| Dùng classic thay quorum (nếu chấp nhận được) | 3,5× trên cụm | Phần 34 |
| Ack theo lô thay vì ack từng cái | +32% | Phần 16 |
| Tăng số consumer khi listener có việc thật | tuyến tính tới 8× | Phần 25 |
Bốn dòng đầu đều nằm trong mã ứng dụng, không phải cấu hình broker. Đó là kết luận lớn nhất của cả sê-ri: gần như mọi vấn đề hiệu năng RabbitMQ mà tôi đo được đều nằm ở cách client dùng nó.
Thứ đo ra không khác gì
Sáu điều thường được khuyên, và con số thật:
| Lời khuyên | Đo ra | Đo ở |
|---|---|---|
| "Dùng transient cho nhanh" | persistent nhanh hơn 31% | Phần 7 |
| "Bớt binding cho exchange nhẹ" | 1 → 500 binding: phẳng | Phần 8, 10 |
| "Viết client thuần thay vì Spring cho nhanh" | chênh 4% | Phần 24 |
| "Dùng default exchange cho đỡ một bước" | chênh 11%, trong nhiễu | Phần 12 |
| "Tắt TLS để lấy thông lượng" | chênh 5% | Phần 37 |
| "Dùng ảnh không có giao diện cho test nhanh" | 2,9 s so với 2,8 s | Phần 30 |
Dòng đầu là dòng tôi thích nhất. Nó đi ngược lời khuyên phổ biến nhất về RabbitMQ, và phần 7 tìm ra lý do bằng một chỉ số: messages_ram = 1 ở cả hai chế độ. Thông điệp transient cũng đã nằm trên đĩa rồi; cờ đó chỉ quyết định thứ được khôi phục sau khi khởi động lại.
Ba dòng cuối đều là đánh đổi an toàn hoặc tiện lợi lấy vài phần trăm — mà vài phần trăm đó nằm trong nhiễu của chính phép đo.
Thứ tự nên làm
1. Sửa vòng đời connection và channel. Một connection cho ứng dụng, một channel cho mỗi luồng. Đây là 1 128 lần, và nó miễn phí.
2. Chọn đúng chế độ confirm. Gửi ít thì confirm từng cái cho đơn giản; gửi nhiều thì bất đồng bộ. Đừng để mặc định "không confirm" trên dữ liệu quan trọng — phần 24 đo được rằng Spring Boot tắt nó theo mặc định.
3. Đặt prefetch theo thời gian xử lý. Listener nhanh thì 100–500; listener gọi ra ngoài thì 1–5. Phần 19 cho thấy đoán sai theo cả hai hướng đều đắt.
4. Đừng chặn luồng consumer để chờ. Mọi Thread.sleep trong listener — kể cả cái nằm trong thư viện retry — là thời gian chết nhân với số luồng.
5. Đếm số đích, không đếm số binding. Câu hỏi đúng khi thiết kế topology là "mỗi thông điệp sẽ được ghi mấy lần", không phải "exchange này nhanh không".
6. Chỉ sau khi làm hết năm việc trên mới đụng tới broker. Trong toàn bộ sê-ri này, tôi không sửa một dòng cấu hình broker nào để lấy hiệu năng — mọi con số ở bảng đầu đều đến từ phía client.
Bài sau là bài cuối: RabbitMQ hay Kafka, và nhìn lại bốn mươi bài.
Thử ba mươi giây
docker exec -u rabbitmq rmq rabbitmqctl -q list_connections user channels
docker exec -u rabbitmq rmq rabbitmqctl -q list_consumers queue_name prefetch_count ack_required
Lệnh đầu: số connection tăng theo tải nghĩa là bạn đang ở dòng số một của bảng. Lệnh sau: prefetch_count bằng 0 hoặc bằng 250 mặc định của Spring nghĩa là chưa ai chọn nó theo thời gian xử lý thật.