Highly Available Queues và Clustering cho RabbitMQ

RabbitMQ là một message broker sử dụng giao thức AMQP - Advanced Message Queue Protocol, đóng vài trò như một phương tiện trung gian để giao tiếp giữa nhiều thành phần trong một hệ thống lớn , ví dụ trên hệ thống OpenStack. RabbitMQ nhận message đến từ các thành phần khác nhau trong hệ thống, lưu trữ chúng an toàn trước khi đẩy đến đích. Để nâng cao tính sẵn sàng, khả năng chịu lỗi, RabbitMQ có thể cấu hình thành 1 nhóm cluster. Tôi sẽ cùng bạn cấu hình Clustering cho RabbitMQ trong bài dưới đây.

I. Một vài lưu ý

Hostnames

Đây là một lưu ý quan trọng khi làm việc với RabbitMQ Cluster. Cách thức mà cluster nhận diện node và giao tiếp với nhau cần được hiểu rõ ràng. Mặc định RabbitMQ sử dụng rabbit@hostname cho tên của node. Không có sự ràng buộc về đặt hostname, miễn sao đó là duy nhất.

RabbitMQ đặt tên thư mục database sử dụng hostname hiện tại của hệ thống. Nếu hostname thay đổi, một empty database được tạo ra. Để tránh mất mát dữ liệu cần cố định hostname và set phân giải ra IP. Ví dụ:

sudo -s # become root  
echo "rabbit" > /etc/hostname  
echo "127.0.0.1 rabbit" >> /etc/hosts  
hostname -F /etc/hostname  

Bất cứ khi nào đổi hostname, cần restart RabbitMQ:

$ /etc/init.d/rabbitmq-server restart

Firewall rules

RabbitMQ mặc định giao tiếp với nhau qua một số port TCP, nên bạn cần lưu ý tạo security group rule allow những port này và đảm bảo instance được gán vào security group rule đó. Nếu không bạn sẽ gặp khó khăn trong việc debug lỗi không cần thiết. Các port được dùng trong RabbitMQ:

  • 4369 (epmd), 25672 (Erlang distribution)
  • 5672, 5671 (AMQP 0-9-1 without and with TLS)
  • 15672 (if management plugin is enabled)
  • 61613, 61614 (if STOMP is enabled)
  • 1883, 8883 (if MQTT is enabled)

Node type

Một node trong RabbitMQ có thể là disk node hoặc RAM node. (Chú ý: disk và disc được sử dụng thay thế cho nhau, ví dụ trong cú pháp file cấu hình hoặc status messages thường sử dụng disc).

RAM nodes chỉ lưu các state của nó trong memory (đối với nội dung của queue cố định hoặc quá lớn để lưu trên memory thì có thể lưu trên disc).

Disk nodes giữ state trên cả memory và disk. Bởi vì RAM nodes không ghi nội dung lên disk như disk nodes, nên chúng hoạt động nhanh hơn. Tuy nhiên, cần lưu ý rằng do queue data luôn được lưu trên disc, nên phần cải thiện performance sẽ chỉ ảnh hưởng tới resources management (như adding/removing queues, exchanges, hoặc vhosts), chứ không cải thiện hiệu năng của publishing hay consuming.

Bởi vì state được replicate trên tất cả các node trong cluster, nên có thể chỉ cần có một disk node trong cluster để lưu state của cluster một cách an toàn. Tuy nhiên điều này không được khuyến nghị do tiềm ẩn nguy cơ mất mát dữ liệu.

Erlang cookie

Erlang nodes sử dụng cookie xác thực cho giao tiếp giữa các node. Cookie là một chuỗi ký tự các chữ cái với độ dài bất kỳ. Erlang tự động tạo file cookie ngẫu nhiên khi RabbitMQ server khởi động. 2 nodes có thể giao tiếp với nhau phải có cùng cookie, nên ta có thể copy file cookie từ một node sang các node còn lại trong cluster.
Trên Linux, file cookie đặt trong đường dẫn /var/lib/rabbitmq/.erlang.cookie hoặc $HOME/.erlang.cookie.

II. Tạo RabbitMQ cluster

Để bắt đầu, ta launch hai instance Ubuntu Server 14.04 với hostname rabbit1rabbit2. Để nâng cao tính dự phòng nên tạo instance ở các zone khác nhau.

Cài đặt RabbitMQ Server

RabbitMQ hỗ trợ hầu hết các nền tảng:

  • Windows
  • Linux/Unix: Debian/Ubuntu, Fedora/RHEL, GenericUnix, Solaris
  • Mac OS X

Trên repo của Ubuntu 14.04 có sẵn gói rabbitmq-server version 3.2.4-1.

sudo apt-get update  
sudo apt-get install rabbitmq-server  

Để cài gói mới nhất phục vụ cho mục đích test, thêm dòng sau vào file /etc/apt/sources.list, sau đó update repo và cài đặt.

deb http://www.rabbitmq.com/debian/ testing main  

Tạo cluster

Tiếp theo, tạo cluster trên rabbit1:

rabbit1$ sudo /etc/init.d/rabbitmq-server stop  
rabbit1$ sudo rabbitmq-server -detached  
rabbit1$ sudo rabbitmqctl stop_app  
rabbit1$ sudo rabbitmqctl reset  
rabbit1$ sudo rabbitmqctl start_app  

Kiểm tra trạng thái cluster bằng lệnh cluster_status

rabbit1$ sudo rabbitmqctl cluster_status  
Cluster status of node rabbit@rabbit1 ...  
[{nodes,[{disc,[rabbit@rabbit1]}]},{running_nodes,[rabbit@rabbit1]}]
...done.

Giờ ta đã có một node running trong cluster, tiếp theo ta sẽ thêm một node khác vào cluster.

Thêm node vào cluster

Thêm rabbit2 vào cluster rabbit@rabbit1

  • Stop RabbitMQ application
  • Join rabbit2 vào rabbit@rabbit1 cluster (option --ram nếu muốn rabbit2 là ram node)
  • Start RabbitMQ application.
rabbit2$ sudo rabbitmqctl stop_app  
rabbit2$ sudo rabbitmqctl reset  
rabbit2$ sudo sudo rabbitmqctl join_cluster rabbit@rabbit1  
rabbit2$ sudo rabbitmqctl start_app  

Hoặc chỉ ra rabbit2 là ram node:

rabbit2$ sudo rabbitmqctl join_cluster --ram rabbit@rabbit1

Kiểm tra trạng thái cluster bằng lệnh cluster_status trên cả 2 node:

rabbit1$ sudo rabbitmqctl cluster_status  
Cluster status of node rabbit@rabbit1 ...  
[{nodes,[{disc,[rabbit@rabbit1]},{ram,[rabbit@rabbit2]}]},
 {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
...done.

III. Seting up Highly Available queues

Mặc định các queue trong RabbitMQ cluster chỉ nằm trên một node duy nhất mà chúng được khai báo trước tiên. Không giống như exchange và binding luôn luôn nằm trên mọi node. Tuy nhiên, queue cũng có thể cấu hình để mirror trên nhiều node. Mỗi "mirrored queue" gồm 1 master và nhiều slave, và slave đầu tiên lên làm master mới nếu master cũ bị mất.

Message được publish tới queue sẽ được replicate đến tất cả các slave. Slave sẽ drop message mà nó nhận được. Do đó Queue mirroring chỉ nâng cao tính sẵn sàng chứ không phân bố tải trên các node.

Cấu hình Mirroring

Cấu hình Queue mirroring được thực hiện thông qua policy. Policie có thể thay đổi mọi thời điểm, đồng nghĩa với việc có thể tạo một non-mirrored queue sau đó biến chúng thành mirrored queue hoặc ngược lại.

Để tạo mirrored queue, ta tạo 1 policy để match và sử dụng key ha-modeha-params để set policy. Dưới đây là một vài ví dụ.

Policy match những queue với tên bắt đầu bằng "ha." sẽ mirror sang tất các node trong cluster:

rabbitmqctl set_policy ha-all "^ha\." '{"ha-mode":"all"}'  

Policy match những queue với tên bắt đầu bằng "two." sẽ mirror sang 2 node bất kỳ trong cluster với option automatic synchronization:

rabbitmqctl set_policy ha-two "^two\." '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'  

Policy match những queue với tên bắt đầu bằng "nodes." sẽ mirror các node được chỉ định cụ thể trong cluster:

rabbitmqctl set_policy ha-nodes "^nodes\." '{"ha-mode":"nodes","ha-params":["nodeA", "nodeB"]}'  

Tài liệu tham khảo

www.rabbitmq.com/clustering.html
www.rabbitmq.com/ha.html