RabbitMQ: durable, persistent và quorum queue

4 câu hỏi~25 phútTừ khóa: durable, persistent, classic queue, mirrored queue, quorum queue, Raft, leader, follower, majority, cluster 3 node, stream

Tiếp đi: RabbitMQ chết thì message có mất không, và quorum queue.

Bài 8.3 lo hai đầu: producer → broker và broker → consumer. Bài này lo phần giữa: broker tự giữ message thế nào khi restart hoặc mất máy.

1. Bài toán: restart RabbitMQ trong giờ cao điểm

shop-app đang có 5.000 OrderPlaced nằm chờ trong notification.order-placed vì email provider chậm. Đội vận hành restart RabbitMQ để nâng version. Rồi một hôm khác, cả VM chạy RabbitMQ chết hẳn ổ đĩa.

2. Vấn đề

3. Giải pháp

Thuộc tínhThuộc vềÝ nghĩa
DurableExchange, queueĐịnh nghĩa còn sau khi broker restart
Persistent (delivery mode 2)MessageMessage được ghi xuống đĩa
ReplicationQueueMessage có bản sao trên nhiều node

Cần cả ba nếu không muốn mất message. Spring AMQP mặc định đã gửi message persistent, và QueueBuilder.durable() tạo queue durable.

Lịch sử replication trong RabbitMQ:

Quorum queue hoạt động thế nào:

4. Code từng bước

Bước 1: Dựng cluster 3 node (docker compose, rút gọn). Các node phải dùng chung Erlang cookie, lấy từ secret/biến môi trường, không ghi cứng:

services:
  rabbit1:
    image: rabbitmq:4-management
    hostname: rabbit1
    environment:
      RABBITMQ_ERLANG_COOKIE: ${RABBIT_COOKIE}
      RABBITMQ_DEFAULT_USER: ${RABBIT_USER}
      RABBITMQ_DEFAULT_PASS: ${RABBIT_PASS}
    volumes: [ "rabbit1-data:/var/lib/rabbitmq" ]
  rabbit2:
    image: rabbitmq:4-management
    hostname: rabbit2
    environment:
      RABBITMQ_ERLANG_COOKIE: ${RABBIT_COOKIE}
    volumes: [ "rabbit2-data:/var/lib/rabbitmq" ]
  rabbit3:
    # tương tự rabbit2

# join rabbit2, rabbit3 vào cluster của rabbit1:
# docker exec rabbit2 rabbitmqctl stop_app
# docker exec rabbit2 rabbitmqctl join_cluster rabbit@rabbit1
# docker exec rabbit2 rabbitmqctl start_app

Production thường dùng RabbitMQ Cluster Operator trên Kubernetes hoặc dịch vụ managed (Amazon MQ, CloudAMQP) thay vì tự join tay.

Bước 2: Config Spring kết nối nhiều node

spring:
  rabbitmq:
    addresses: rabbit1:5672,rabbit2:5672,rabbit3:5672
    username: ${RABBIT_USER}
    password: ${RABBIT_PASS}
    publisher-confirm-type: correlated

Client thử lần lượt các địa chỉ, node nào sống thì kết nối. Spring AMQP tự kết nối lại khi mất connection.

Bước 3: Khai báo quorum queue

@Bean
Queue inventoryOrderPlaced() {
    return QueueBuilder.durable("inventory.order-placed")
            .quorum()                 // x-queue-type=quorum
            .deliveryLimit(5)         // chặn requeue loop (bài 8.3)
            .build();
}

Không đổi được loại queue của queue đã tồn tại: declare lại với x-queue-type khác sẽ lỗi PRECONDITION_FAILED. Muốn chuyển classic → quorum phải tạo queue mới, chuyển binding, cho consumer đọc hết queue cũ rồi xóa.

Có thể ép mọi queue mới thành quorum ở phía server bằng cấu hình default_queue_type = quorum cho virtual host.

Bước 4: Test failover

docker exec rabbit1 rabbitmq-queues quorum_status inventory.order-placed
# thấy leader trên rabbit1, follower rabbit2, rabbit3

docker stop rabbit1
curl -X POST http://localhost:8081/orders -d '...' -H "Content-Type: application/json"
# vẫn đặt hàng được; leader mới trên rabbit2 hoặc rabbit3; inventory vẫn nhận event

docker stop rabbit2
# chỉ còn 1/3 node → mất quorum → queue không nhận message mới, confirm không về
# producer nhận timeout confirm → Outbox giữ event lại, gửi sau

5. Cách hoạt động

producer ── publish ──> rabbit1 (leader)
                           │ append log
                           ├──> rabbit2 (follower)  ghi ✅
                           └──> rabbit3 (follower)  chậm...
                 2/3 đã ghi = majority
producer <── confirm ack ──┘

rabbit1 chết:
  rabbit2, rabbit3 bầu leader mới (Raft election) → queue tiếp tục
  rabbit1 sống lại → thành follower, đồng bộ phần log thiếu

Vì sao cluster 3 node? Quorum của N node là N/2 + 1:

Số nodeQuorumChịu được mất
110
220 (tệ như 1 node)
321
532

Số lẻ là hợp lý: 4 node vẫn chỉ chịu mất 1, tốn thêm máy mà không thêm khả năng chịu lỗi.

6. So sánh

Tiêu chíClassic queueQuorum queueStream
ReplicationKhông (mirrored đã bị gỡ ở 4.0)Raft, majorityCó
Sau khi consumeXóaXóaGiữ theo retention, đọc lại bằng offset
Delivery limit (chống poison)KhôngCóKhông áp dụng
Non-durable / exclusiveCóKhông, luôn durableKhông
PriorityCó (x-max-priority)Có từ 4.0 (2 mức: cao / thường)Không
Phù hợpQueue tạm, dữ liệu không quan trọngEvent nghiệp vụ cần an toànFan-out lớn, replay, lưu lâu

7. Khi nào nên dùng / không nên dùng

Cluster 3 node thì một queue có được chia ra 3 node để xử lý nhanh gấp 3 không?

Không, đây là điểm hay bị nhầm với Kafka partition. Một quorum queue là một Raft group: mọi publish và deliver đều đi qua leader. Follower chỉ giữ bản sao để chịu lỗi, không chia tải.

queue inventory.order-placed
  leader   : rabbit1   ← mọi read/write
  follower : rabbit2, rabbit3 (bản sao)

Client kết nối rabbit2 → rabbit2 chuyển tiếp nội bộ tới leader trên rabbit1

Cluster giúp tăng throughput tổng bằng cách đặt leader của nhiều queue khác nhau lên các node khác nhau:

rabbit1: leader inventory.order-placed
rabbit2: leader notification.order-placed
rabbit3: leader audit.all

Một queue quá nóng thì phải tự chia thành nhiều queue (ví dụ consistent hash exchange theo orderId) hoặc dùng super stream. Đây là chỗ Kafka mạnh hơn vì partition là khái niệm có sẵn.

Vậy cluster bị chia đôi mạng, 2 node một bên, 1 node một bên thì sao? Có bị hai leader không?

Không, đó chính là lý do dùng Raft. Chỉ phía có đa số mới bầu được leader và nhận ghi:

        network partition
 [rabbit1] ║ [rabbit2  rabbit3]
  1/3 node ║  2/3 node = majority
  không ghi được   bầu leader, tiếp tục chạy
  (không confirm)

Phía thiểu số không confirm message nào, nên không có chuyện hai bên cùng nhận ghi rồi xung đột (split-brain). Khi mạng nối lại, rabbit1 quay về làm follower và đồng bộ log. Đổi lại: nếu mất đa số, queue ngừng nhận ghi (chọn consistency hơn availability). Producer phải chịu được việc publish thất bại, ví dụ nhờ Outbox.

Phỏng vấn hỏi về độ bền message và quorum queue thì trả lời sao?

Q1. Làm sao để RabbitMQ restart không mất message?

🇻🇳 Tiếng Việt:

Cần exchange và queue durable để định nghĩa còn sau restart, message persistent để được ghi đĩa, và publisher confirm để producer biết broker đã lưu. Để chịu được mất cả một node thì dùng quorum queue trên cluster ít nhất 3 node.

🇬🇧 English:

Exchanges and queues must be durable so their definitions survive a restart, messages must be persistent so they are written to disk, and publisher confirms tell the producer the broker has stored them. To survive losing a whole node, use quorum queues on a cluster of at least three nodes.

Q2. Quorum queue là gì, khác mirrored queue thế nào?

🇻🇳 Tiếng Việt:

Quorum queue là queue được replicate bằng Raft: một leader, nhiều follower, message chỉ được confirm khi đa số node đã ghi. Leader chết thì follower bầu leader mới. Classic mirrored queue là cơ chế cũ, có vấn đề khi network partition và đồng bộ lại, đã bị gỡ trong RabbitMQ 4.0. Quorum queue còn có delivery limit để chặn poison message.

🇬🇧 English:

A quorum queue is replicated with Raft: one leader and several followers, and a message is confirmed only after a majority of nodes have written it. If the leader fails, the followers elect a new one. Classic mirrored queues were the old approach; they had problems with network partitions and resynchronisation and were removed in RabbitMQ 4.0. Quorum queues also have a delivery limit that stops poison messages from looping.

Q3. Vì sao cluster 3 node? Quorum queue có tăng throughput không?

🇻🇳 Tiếng Việt:

3 node có quorum là 2 nên chịu được mất 1 node; 2 node thì không chịu được node nào. Quorum queue không chia tải trong một queue, mọi thao tác đi qua leader. Throughput tổng tăng nhờ phân bố leader của nhiều queue lên nhiều node.

🇬🇧 English:

With three nodes the quorum is two, so we can lose one node; with two nodes we cannot lose any. A quorum queue does not spread load within one queue, because every operation goes through the leader. Overall throughput grows by spreading the leaders of different queues across nodes.

Cách nhớ ngắn gọn


Tự kiểm tra

Queue durable nhưng message không persistent, restart broker thì sao?

Queue còn, message mất (chỉ nằm trong RAM).

Cluster 2 node chạy quorum queue, một node chết thì sao?

Còn 1/2 không đủ majority, queue ngừng nhận ghi. Vì vậy cần ít nhất 3 node.

Đổi queue classic có sẵn thành quorum bằng cách declare lại được không?

Không, báo PRECONDITION_FAILED. Phải tạo queue mới, chuyển binding, xả queue cũ.

Khi nào publisher confirm về với quorum queue?

Khi đa số replica đã ghi message vào log.

Stream khác quorum queue ở điểm chính nào?

Stream giữ message sau khi đọc, consumer đọc lại bằng offset (giống Kafka); quorum queue xóa message sau ACK.