Phần 34 nối ba node thành một cụm — chúng chia sẻ metadata và cần mạng ổn định. Bài này nối hai broker hoàn toàn rời nhau: hai cụm khác nhau, có thể ở hai trung tâm dữ liệu, chỉ nói chuyện qua AMQP thường.

Hai công cụ, và khác biệt giữa chúng nằm ở đúng một chữ.

Shovel: chuyển thông điệp

docker exec -u rabbitmq bkA rabbitmqctl set_parameter shovel chuyen-sang-B '{
  "src-protocol":"amqp091", "src-uri":"amqp://",        "src-queue":"nguon",
  "dest-protocol":"amqp091","dest-uri":"amqp://guest:guest@bkB","dest-queue":"dich"}'

Gửi 20 000 thông điệp vào nguon của broker A:

trước khi gửi : A:nguon=0      B:dich=0
sau 12 giây   : A:nguon=0      B:dich=20000

Toàn bộ thông điệp đã sang B, và A không còn gì. Shovel là một consumer bình thường: nó đọc từ hàng đợi nguồn rồi publish sang đích, nên thông điệp rời khỏi nguồn.

Tốc độ chuyển, đo bằng cách nạp sẵn 50 000 rồi bật shovel:

chuyển 50 000 thông điệp sang broker khác: 1,2 s (40 234 msg/s)

Nhanh, nhưng nhớ rằng đó là một consumer đơn: nó không nhân lên theo số node, và với thông điệp lớn hoặc đường truyền xa thì con số này sẽ khác hẳn.

Federation: chép thông điệp

Federation làm điều ngược lại. Trên broker B (bên nhận), khai một upstream trỏ về A rồi gắn policy lên exchange:

docker exec -u rabbitmq bkB rabbitmqctl set_parameter federation-upstream tu-A \
  '{"uri":"amqp://guest:guest@bkA","expires":3600000}'
docker exec -u rabbitmq bkB rabbitmqctl set_policy fed-su-kien "^su-kien$" \
  '{"federation-upstream":"tu-A"}' --priority 1 --apply-to exchanges

Gửi 1 000 thông điệp vào exchange su-kien của A:

A:nghe-A=1000   B:nghe-B=1000

Cả hai bên đều nhận đủ. Bên nghe ở A không mất gì, còn B có thêm bản của mình.

Cơ chế lộ ra khi nhìn danh sách hàng đợi của A:

name                                 messages
nghe-A                               1000
federation: su-kien -> rabbit@bkB    0

Federation tự tạo một hàng đợi trên broker nguồn, bind vào exchange cần chép, rồi kéo về. Biết điều này rất có ích lúc gỡ lỗi: nếu hàng đợi đó phình lên, nghĩa là đường truyền sang B đang không theo kịp.

Federation cần plugin ở cả hai broker

Lần đầu tôi chỉ bật rabbitmq_federation trên B — bên nhận, nơi link chạy. Kết quả:

trạng thái: {server_initiated_close, 404,
  "NOT_FOUND - no exchange 'federation: su-kien -> rabbit@bkB B' in vhost '/'"}
log: Federation link could not create a disposable (one-off) connection: function_clause

Bật plugin trên A nữa thì mọi thứ chạy ngay. Thông báo lỗi không hề gợi ý điều đó — nó nói về một exchange không tồn tại, khiến người ta đi tìm sai chỗ.

Shovel thì ngược lại: chỉ cần plugin ở phía chạy shovel, đầu kia không cần biết gì cả, vì với nó shovel chỉ là một client AMQP bình thường.

Bảng so sánh

Shovel Federation
Thông điệp ở nguồn bị lấy đi còn nguyên
Đơn vị nối hàng đợi → hàng đợi (hoặc exchange) exchange → exchange (hoặc hàng đợi)
Khai ở đâu broker chạy shovel broker nhận
Plugin cần bật một phía cả hai phía
Dấu vết trên nguồn không một hàng đợi federation: ...

Dùng để làm gì

Di trú không dừng dịch vụ. Đây là chỗ shovel toả sáng. Dựng broker mới, shovel hàng đợi cũ sang, đợi nguon=0, rồi chuyển consumer sang broker mới. Không mất thông điệp, không có cửa sổ dừng — và nếu hỏng thì chỉ cần xoá shovel.

Chuyển vùng địa lý. Federation đúng hơn khi mỗi vùng cần bản sao của cùng một luồng sự kiện mà vẫn phục vụ được người dùng tại chỗ. Mỗi vùng có exchange riêng, nghe upstream của vùng kia.

Gom về trung tâm. Nhiều broker biên, một broker trung tâm: shovel từng broker biên đẩy về, vì dữ liệu chỉ cần đến đúng một nơi.

Và nhớ điều phần 22 đo được: cả hai cơ chế đều là at-least-once. Đường truyền đứt giữa lúc chuyển thì thông điệp có thể sang hai lần — consumer phía bên kia vẫn phải lũy đẳng.

Bài sau: vhost, phân quyền và TLS.

Thử ba mươi giây

docker exec -u rabbitmq bkA rabbitmqctl shovel_status
docker exec -u rabbitmq bkB rabbitmqctl federation_status

Với shovel, cột cuối là số thông điệp đã chuyển. Với federation, status khác running nghĩa là đường nối đang hỏng — và thông điệp vẫn dồn trong hàng đợi federation: ... ở đầu bên kia.