Phần trước đo quorum queue trên một node và kết bằng một cảnh báo: cụm thật sẽ đắt hơn. Bài này dựng cụm ba node rồi trả con số đó.
Dựng cụm ba node
docker network create rmqnet
for i in 1 2 3; do
docker run -d --name n$i --hostname n$i --network rmqnet \
-e RABBITMQ_ERLANG_COOKIE=MOT_CHUOI_BI_MAT \
-p $((5670+i)):5672 -p $((15670+i)):15672 rabbitmq:4-management
done
for i in 2 3; do
docker exec -u rabbitmq n$i rabbitmqctl stop_app
docker exec -u rabbitmq n$i rabbitmqctl join_cluster rabbit@n1
docker exec -u rabbitmq n$i rabbitmqctl start_app
done
Ba điều bắt buộc: cùng một erlang cookie, hostname cố định (nhớ phần 2: tên node là rabbit@<hostname>), và -u rabbitmq khi gọi CLI — cũng phần 2, gọi bằng root lúc broker đang lên sẽ giết chính nó.
Cái giá thật của nhân bản
Cùng phép đo với phần trước — 50 000 thông điệp 200 byte, persistent, có publisher confirms:
| Một node | Cụm ba node | |
|---|---|---|
| classic | 127 800 msg/s | 136 658 msg/s |
| quorum | 81 967 msg/s | 39 182 msg/s |
Classic không đổi — nó chỉ sống trên một node, cụm hay không cũng thế. Quorum thì mất thêm một nửa so với chính nó ở chế độ một node, và chỉ còn 29% so với classic.
Mất một node
Tạo một hàng đợi quorum (qq3) và một hàng đợi classic tạo từ n2 (cq2), rồi tắt n2:
đủ ba node:
qq3 qua n1: gửi ĐƯỢC, hàng đợi có 101
cq2 qua n1: gửi ĐƯỢC, hàng đợi có 101
sau khi tắt n2:
qq3 qua n1: gửi ĐƯỢC, hàng đợi có 102
cq2 qua n1: channel 404 NOT_FOUND - queue 'cq2' in vhost '/' process is stopped by supervisor
Đây là toàn bộ lý do quorum queue tồn tại, gói trong hai dòng. Hàng đợi classic sống trên đúng một node; node đó chết là hàng đợi biến mất khỏi cụm — không phải mất dữ liệu, nhưng không dùng được cho tới khi node quay lại. Hàng đợi quorum có bản sao ở cả ba node nên mất một node không ai để ý.
Mất đa số
Tắt tiếp n3, chỉ còn một trong ba node:
gửi + CHỜ XÁC NHẬN: HẾT GIỜ, broker không xác nhận
Đúng như thiết kế: không có đa số thì không được phép nói "đã ghi". Bật lại n2 để có 2/3:
gửi + CHỜ XÁC NHẬN: BROKER ĐÃ NHẬN sau 0,0 s
Lưu ý cách đo. Lần đầu tôi thử bằng basicPublish trần và thấy "gửi được" ở cả hai trường hợp — vì phần 17 đã đo rằng basicPublish không hứa gì cả, nó chỉ ghi vào socket. Chỉ khi bật confirms thì sự khác biệt mới hiện ra. Trên cụm, gửi không có confirms là tự bịt mắt.
Cắt mạng thật
Tắt node là một chuyện; node còn sống mà không liên lạc được là chuyện khác. Ngắt n3 khỏi mạng Docker trong khi nó vẫn chạy:
nhìn từ n1 (đa số): Running Nodes = rabbit@n1 rabbit@n2
nhìn từ n3 (thiểu số): Running Nodes = rabbit@n3
gửi vào qq3 qua bên đa số (n1) : BROKER ĐÃ NHẬN sau 0,0 s
gửi vào qq3 qua bên thiểu số (n3) : HẾT GIỜ, broker không xác nhận
Hai bên nhìn thấy hai thế giới khác nhau, và chỉ bên có đa số được phép ghi. Nối mạng lại thì cụm tự lành, Network Partitions: (none), và qq3 giữ đúng 104 thông điệp — chính xác những cái đã được xác nhận, không thừa không thiếu.
Điều tôi không đo được
Câu hỏi còn lại là hàng đợi classic ở bên thiểu số: nó có tiếp tục nhận thông điệp không, tạo ra hai bản sự thật? Tôi không đo được trong môi trường này — docker network disconnect gỡ luôn giao diện mạng của container, nên cổng publish ra host cũng mất và tôi không còn đường nào nối vào n3 để thử.
Vì vậy bài này không đưa ra con số nào cho pause_minority so với autoheal. Điều đo được là: cluster_partition_handling không được đặt tường minh trong image chính thức, tức cụm của bạn đang chạy giá trị mặc định — và nếu bạn còn dùng hàng đợi classic cho dữ liệu quan trọng, đó là thứ đáng đọc kỹ tài liệu rồi đặt tường minh, chứ đừng để mặc.
Với hàng đợi quorum thì câu hỏi đó gần như không còn ý nghĩa: luật đa số của Raft đã trả lời sẵn, và phép đo ở trên cho thấy nó trả lời đúng.
Ba điều mang đi
Cụm không làm hàng đợi classic sẵn sàng hơn. Nó vẫn sống trên một node. Cụm chỉ cho bạn chỗ để đặt bản sao — và chỉ quorum queue với stream mới dùng chỗ đó.
Ba node là con số tối thiểu có nghĩa. Hai node thì mất một là mất đa số, tức không khá hơn một node.
Luôn bật confirms trên cụm. Không có nó thì mọi thứ ở trên trông y hệt nhau: thành công.
Bài sau: stream — kiểu hàng đợi thứ ba, cũng nhân bản qua cụm nhưng theo mô hình hoàn toàn khác.
Thử ba mươi giây
docker exec -u rabbitmq n1 rabbitmqctl -q list_queues name type members
name type members
sq stream [rabbit@n1, rabbit@n2, rabbit@n3]
qq3 quorum [rabbit@n3, rabbit@n1, rabbit@n2]
cq2 classic [rabbit@n2]
cq3 classic [rabbit@n3]
Bốn dòng đó là cả bài này thu gọn. Hàng đợi nào chỉ có một node trong cột members là hàng đợi sẽ biến mất khỏi cụm khi node đó chết — và cụm ba node không giúp gì được cho nó.