Nỗi sợ kinh điển khi vận hành RabbitMQ: consumer chết, hàng đợi dồn lên hàng triệu thông điệp, broker hết RAM và kéo sập mọi thứ. Bài này đẩy thật một triệu thông điệp vào rồi đo xem chuyện đó có xảy ra không.

Bảng đo

Thông điệp 200 byte, persistent, hàng đợi classic mặc định:

Số thông điệp Bộ nhớ broker Đĩa message_bytes messages_ram Tốc độ gửi
0 202 MiB 1 MB
100 000 38 MB 19 MiB 1 128 554 msg/s
500 000 221 MiB 188 MB 95 MiB 1 135 022 msg/s
1 000 000 221 MiB 376 MB 191 MiB 1 127 659 msg/s

Ba điều bảng đó nói

Bộ nhớ đứng yên. Từ 500 nghìn lên một triệu thông điệp, mem_used không nhúc nhích: 221 MiB cả hai lần, so với 202 MiB lúc broker trống. Một triệu thông điệp chứa 191 MiB dữ liệu chỉ làm broker tốn thêm khoảng 19 MiB RAM.

messages_ram luôn bằng 1. Phần 7 đã thấy con số này ở quy mô 20 nghìn và tôi đã dùng nó để giải thích vì sao persistent không chậm hơn transient. Ở quy mô một triệu nó vẫn là 1: classic queue hiện đại giữ đúng một thông điệp trong RAM, phần còn lại nằm trên đĩa.

Thông lượng không giảm. 128k, 135k, 128k msg/s — hàng đợi có một nghìn hay một triệu thông điệp thì tốc độ ghi vào như nhau.

Nỗi sợ "hàng đợi dồn lại làm broker hết RAM" thuộc về RabbitMQ đời cũ, khi classic queue giữ thông điệp trong bộ nhớ cho tới lúc bị áp lực mới đẩy xuống đĩa. Với bản hiện tại, cái phình lên là đĩa, không phải RAM.

Rút hết một triệu

rút hết trong 9,0 s (110 792 msg/s)

Không có hiện tượng "hàng đợi quá lớn nên chậm". Chín giây cho một triệu thông điệp, với prefetch 500 và ack theo lô — đúng vùng thông lượng mà phần 19 đo được cho hàng đợi rỗng.

Đĩa mới là thứ tăng

Cột đĩa tăng tuyến tính và gấp khoảng hai lần dữ liệu thật: 191 MiB thông điệp chiếm 376 MB trên đĩa. Phần dôi ra là chỉ mục, siêu dữ liệu mỗi thông điệp và các đoạn tệp chưa được dọn.

Con số để mang đi: hàng đợi tốn khoảng gấp đôi kích thước dữ liệu của nó trên đĩa. Một triệu thông điệp 1 KB là khoảng 2 GB — và đó mới là một hàng đợi.

x-queue-mode: lazy giờ không còn nghĩa gì

khai x-queue-mode=lazy -> chấp nhận (không lỗi)

Broker vẫn nhận tham số này, nhưng bảng đo ở trên cho thấy vì sao nó thừa: hàng đợi thường đã hành xử như lazy rồi. Chế độ lazy sinh ra để ép thông điệp xuống đĩa thay vì giữ trong RAM; giờ đó là mặc định.

Đây là kiểu thay đổi khó nhận ra nhất khi nâng cấp: cấu hình cũ không báo lỗi, chỉ lặng lẽ trở thành vô nghĩa. Nếu bạn còn đặt x-queue-mode ở đâu đó, hãy bỏ đi — giữ lại chỉ làm người sau tưởng nó đang có tác dụng.

Vậy còn phải lo gì

Bộ nhớ không còn là mối lo hàng đầu, nhưng ba thứ khác thì có:

Đĩa đầy. RabbitMQ có ngưỡng đĩa trống; chạm ngưỡng là broker chặn mọi producer lại. Đó là cách nó tự vệ, và với ứng dụng thì nó biểu hiện thành basicPublish treo — chứ không phải một lỗi rõ ràng.

Thời gian rút. Chín giây cho một triệu là nhanh, nhưng nếu consumer của bạn mất 20 ms mỗi thông điệp như phần 25, thì một triệu thông điệp là năm tiếng rưỡi với 8 consumer. Tồn đọng không giết broker; nó giết SLA.

Hàng đợi bị bỏ quên. Phần 15 đo được khoảng 65 KB mỗi hàng đợi rỗng, và gợi ý cách tìm: hàng đợi có consumers=0messages lớn. Đó mới là chỗ đĩa của bạn biến mất.

Cách chặn từ đầu là x-max-length hoặc x-max-length-bytes cộng một dead letter exchangephần 14 đo được rằng thiếu DLX thì hàng đợi đầy sẽ âm thầm vứt thông điệp cũ nhất.

Bài sau: quorum queue — cùng phép đo này, với con số khác hẳn.

Thử ba mươi giây

# hang doi nao dang an dia nhieu nhat, va co ai rut khong
docker exec -u rabbitmq rmq rabbitmqctl -q list_queues \
    name messages message_bytes consumers | sort -k3 -rn | head

Nhân cột message_bytes với hai để ước lượng chỗ nó chiếm trên đĩa. Dòng nào consumers=0 là dòng sẽ còn lớn mãi.