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.