Message Broker

Trong các hệ thống phân tán hiện đại (microservices, cloud), giao tiếp đồng bộ giữa hai ứng dụng đòi hỏi cả hai phải luôn hoạt động ổn định và kết nối suốt thời gian trao đổi thông tin. Điều này có nghĩa là khi ứng dụng A gửi yêu cầu tới ứng dụng B, nếu B đang bận hoặc gặp lỗi thì A sẽ phải chờ hoặc thất bại.

Ví dụ trong thực tế, một hệ thống bán hàng trực tuyến có hai service chính liên quan trực tiếp khi một khách hàng đặt hàng:

  • Front End Service: giao diện web/mobile — nhận yêu cầu đặt hàng từ khách, hiển thị kết quả.
  • Order Service: thực hiện các bước hậu cần để hoàn tất đơn: kiểm tra tồn kho (kho nội bộ hoặc gọi API nhà cung cấp), tính toán khuyến mãi/giảm giá, gọi cổng thanh toán để trừ tiền, tạo hóa đơn, và gửi email/Xuất vé/Gửi lệnh giao hàng tới đối tác vận chuyển.

Với giao tiếp đồng bộ, FE gửi yêu cầu HTTP tới Order Service và chờ tới khi service này trả về kết quả. Khi đó Order Service phải gọi nhiều bên (kiểm kho, thanh toán, in hóa đơn, gọi dịch vụ gửi email, v.v.) thì thời gian xử lý có thể kéo dài nhiều giây hoặc chục giây. Trong lúc đó trang web/mobile đang “đóng băng” chờ kết quả dẫn đến giảm trải nghiệm người dùng.

Hơn nữa, mô hình đồng bộ không có khả năng hấp thụ đột biến tải. Nếu lưu lượng đặt đơn hàng tăng lên sẽ có một lượng lớn yêu cầu đến FE service từ đó nó cũng tạo ra một số lượng lớn các yêu cầu đến với Order ServiceOrder Service gồm rất nhiều tác vụ phức tạp và tốn thời gian có thể dẫn đến nghẽn hệ thống, gây timeout, gián đoạn hàng loạt.

Để khắc phục các vấn đề của giao tiếp đồng bộ, một giải pháp phổ biến là Message Broker — một thành phần trung gian cho phép trao đổi thông điệp ở chế độ bất đồng bộ. Message broker là một module trung gian nhận message từ người gửi (producer), thực hiện kiểm tra/định tuyến nếu cần, và chuyển tiếp message tới các dịch vụ nhận (consumer).

Nói cách khác, producer chỉ cần gửi message tới broker rồi tiếp tục công việc (fire-and-forget); việc phân phối và xử lý message sẽ do broker phối hợp với các consumer đảm nhiệm khi chúng có tài nguyên sẵn sàng. Nhờ vậy, hai bên được tách rời và giảm phụ thuộc lẫn nhau.

Áp dụng vào ví dụ hệ thống bán hàng trực tuyến: khi khách đặt hàng, Front End (FE) có thể tạo đơn tạm và publish event order.created lên broker, rồi trả ngay cho người dùng thông báo “Đơn của bạn đã được tiếp nhận — chúng tôi sẽ gửi email xác nhận khi đơn hoàn tất.” Việc kiểm kho, thanh toán, tạo hóa đơn, và gửi email sẽ được các service hậu cần (inventory, payment, notification…) xử lý bất đồng bộ sau đó.

Chúng ta cũng có thể chia service order thành nhiều service, mỗi service đại diện cho một giai đoạn trong quá trình xử ly đơn hàng và từng cặp service này cũng được tách biệt với nhau nhờ vào message broker.

Hầu hết message broker hỗ trợ hai mô hình cơ bản:

  • Queue (point-to-point): mỗi message vào queue chỉ được một consumer lấy ra và xử lý. Queue giống như một hàng đợi công việc — nhiều worker có thể cùng đọc từ một queue để cân bằng tải, nhưng mỗi message chỉ đến một worker duy nhất. Dùng khi ta muốn phân phối công việc và đảm bảo một nhiệm vụ chỉ được xử lý một lần.
  • Topic / Publish–Subscribe (pub/sub): mỗi message publish lên topic sẽ được gửi bản sao tới tất cả các subscriber. Dùng khi ta muốn phát thông báo đến nhiều service cùng lúc (ví dụ: logging, analytics, notification).

Áp vào ví dụ order.created: khi Front End publish event order.created ta có hai cách xử lý phổ biến:

  1. Broadcast (pub/sub) — broker gửi bản copy của sự kiện đến mọi subscriber: InventoryService, NotificationService, AnalyticsService… Mỗi service nhận và xử lý độc lập.
  2. Fan-out → queues — broker phát bản copy vào mỗi queue riêng cho từng service (ví dụ inventory-queue, payment-queue, notification-queue). Mỗi queue có thể có nhiều worker để scale, nhưng mỗi bản copy vẫn chỉ được tiêu thụ bởi một worker của queue đó.

Ưu điểm nổi bật là ta có thể thêm subscriber mới (ví dụ service phân tích, push notification, gửi khảo sát) chỉ bằng cách subscribe vào topic/queue liên quan — không cần thay đổi Front End hay producer. Đồng thời, vì mỗi service có queue riêng (nếu cần), ta có thể scale từng phần xử lý độc lập (ví dụ thêm worker cho payment mà không ảnh hưởng notification).

Một lợi ích nữa khi sử dụng message broker nữa đó là khả năng buffer message để hấp thụ các đợt tăng đột biến về thông lượng.

Broker cho phép đệm tin nhắn trong các hàng đợi cho đến khi hệ thống sẵn sàng xử lý. Khi lưu lượng tăng vọt, các tin nhắn mới được đưa vào hàng đợi, tránh làm nghẽn ngay phía nhận. Message queues giúp bảo vệ downstream bằng cách đệm các payload đầu vào với tốc độ phù hợp với mức độ tiêu thụ của dịch vụ . Tương tự, Các hàng đợi tin nhắn cho phép hệ thống mở rộng (scale out) và xử lý lưu lượng cao mà không mất dữ liệu. Trên thực t, message queues giúp giải quyết lưu lượng đột biến bằng cách đệm tin nhắn và kiểm soát tốc độ xử lý, ngăn ngừa quá tải và gián đoạn dịch vụ.

Như vậy chúng ta có thể thấy được những thuộc tính chất lượng của hệ thống dùng message broker như sau:

  • Chịu lỗi (fault tolerance): Do các service được tách biệt thông qua broker, nếu một phần của hệ thống tạm thời không sẵn sàng, các phần còn lại vẫn tiếp tục hoạt động bình thường. Thông điệp được lưu tại broker cho đến khi consumer sẵn sàng xử lý. Nhờ vậy, hệ thống tránh mất tin nhắn và có thể khôi phục sau sự cố: khi một service hỏng, chúng ta khởi tạo lại nó, và broker sẽ “bắt kịp” gửi nốt thông điệp cũ. Việc này giảm thời gian chết so với mô hình trực tiếp. Theo Red Buffer, điểm mạnh của message queues là “khả năng cô lập lỗi – khi producer và consumer hoạt động độc lập, lỗi ở một bên không ảnh hưởng bên kia. Hệ thống chỉ mất throughput tạm thời” .
  • Khả năng mở rộng (scalability): Với message broker, ta có thể thêm nhiều consumer để xử lý tăng tải mà không cần thay đổi logic giữa các service. Như Red Buffer đã nêu, việc tách producers và consumers cho phép tăng số lượng đồng thời một cách linh hoạt . Khi có nhu cầu, chỉ cần thêm các instance mới của service subscriber, chúng sẽ tự lấy tin nhắn từ queue mà không cần thông báo lại cho sender.
  • Sẵn sàng cao (high availability): Nhờ lưu giữ tin nhắn trên hệ thống đáng tin cậy, broker giúp đảm bảo thông điệp không bị mất kể cả khi một thành phần gặp lỗi. Hệ thống tổng thể nhờ đó luôn sẵn sàng phục vụ, bởi khi một microservice được sửa chữa/restart, những message trong queue vẫn ở đó để tiếp tục xử lý. Ví dụ, nếu dịch vụ thanh toán offline do sự cố, các lệnh thanh toán vẫn nằm trong queue và sẽ được xử lý ngay khi dịch vụ hồi phục, đảm bảo không có yêu cầu nào “bị rớt” trong lúc downtime.

Giải pháp Message Broker phổ biến:

  • Apache Kafka: Một nền tảng streaming phân tán mã nguồn mở, nổi tiếng về độ thông lượng cao và khả năng chịu lỗi tuyệt vời . Kafka được thiết kế để xử lý khối lượng dữ liệu rất lớn theo thời gian thực, với độ bền và đảm bảo tin nhắn không mất.
  • RabbitMQ: Message broker mã nguồn mở phổ biến, hỗ trợ nhiều patterns messaging (điểmđiểm, pub/sub, request-reply…) . RabbitMQ được đánh giá linh hoạt, ổn định và hỗ trợ nhiều ngôn ngữ lập trình. Ban đầu nó được xây dựng trên giao thức AMQP và viết bằng Erlang, rất phù hợp cho các hệ thống message queue .
  • ActiveMQ, Apache Pulsar,…: Ngoài ra còn có ActiveMQ, Pulsar, NATS,… là các giải pháp mã nguồn mở với đặc điểm riêng. (Ví dụ, Kafka/Pulsar ưu tiên throughput rất cao, còn RabbitMQ/ ActiveMQ linh hoạt hơn về patterns.)
  • Amazon SQS (Cloud): Dịch vụ hàng đợi tin nhắn được quản lý hoàn toàn trên AWS. SQS cho phép tách rời các microservices và hệ thống phân tán với khả năng scale tự động, duy trì tin nhắn lâu dài (đến 14 ngày), và hỗ trợ dead-letter queue .

Trong số các implement của message broker ở trên, chúng ta sẽ thử tìm hiểu về RabbitMQ để xem ứng dụng message broker được triển khai như thế nào.

RabbitMQ

RabbitMQ như đã nêu trước đó là một message broker mã nguồn mở phổ biến (dựa trên Erlang), giúp nhận, lưu, định tuyến và giao tiếp các message giữa các ứng dụng theo mô hình bất đồng bộ. Nó hỗ trợ nhiều pattern (point-to-point, pub/sub, routing, topic, RPC…), có UI quản lý (plugin management), và có nhiều client cho các ngôn ngữ (PHP, Python, Java, …). RabbitMQ phù hợp cho hệ thống cần độ tin cậy, đệm tin nhắn khi spike, tách producer/consumer rõ ràng và hỗ trợ đa dạng kiểu routing.

Các thành phần chính:

  • Producer: ứng dụng/tiến trình gửi message.
  • Exchange: nơi producer gửi message đến; exchange chịu trách nhiệm định tuyến message tới queue dựa trên loại exchange và routing key.
  • Queue: nơi lưu message (buffer). Consumer lấy message từ queue để xử lý.
  • Binding: liên kết giữa exchange và queue, có thể chứa binding key để lọc routing.
  • Channel: kênh logic trên một kết nối TCP tới RabbitMQ; hầu hết thao tác thực hiện trên channel để hiệu năng tốt hơn.

Cài đặt RabbitMQ:

Bước 1: Cài đặt các gói cần thiết
sudo apt-get install curl gnupg apt-transport-https -y

Bước 2: Thêm key của RabbitMQ
curl -1sLf "https://keys.openpgp.org/vks/v1/by-fingerprint/0A9AF2115F4687BD29803A206B73A36E6026DFCA"
| sudo gpg --dearmor | sudo tee /usr/share/keyrings/com.rabbitmq.team.gpg > /dev/null

Bước 3: Thêm repository RabbitMQ & Erlang

sudo tee /etc/apt/sources.list.d/rabbitmq.list <<EOF
deb [arch=amd64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb1.rabbitmq.com/rabbitmq-erlang/ubuntu/noble noble main
deb [arch=amd64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb2.rabbitmq.com/rabbitmq-erlang/ubuntu/noble noble main

deb [arch=amd64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb1.rabbitmq.com/rabbitmq-server/ubuntu/noble noble main
deb [arch=amd64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb2.rabbitmq.com/rabbitmq-server/ubuntu/noble noble main
EOF

Bước 4: Cập nhật và cài đặt Erlang + RabbitMQ

sudo apt-get update -y

sudo apt-get install -y erlang-base
erlang-asn1 erlang-crypto erlang-eldap erlang-ftp erlang-inets
erlang-mnesia erlang-os-mon erlang-parsetools erlang-public-key
erlang-runtime-tools erlang-snmp erlang-ssl
erlang-syntax-tools erlang-tftp erlang-tools erlang-xmerl

sudo apt-get install rabbitmq-server -y --fix-missing

Bước 5: Start server

sudo systemctl start rabbitmq-server

Bước 6: Kiểm tra trạng thái server

sudo systemctl status rabbitmq-server

Demo

Sau khi đã cài đặt xong RabbitMQ, ta sẽ cùng xây dựng một pet project bằng PHP gồm Producer (gửi message) và Worker (nhận và xử lý message) để xem RabbitMQ hoạt động như thế nào trong vai trò một message broker.

Ở đây ta sẽ viết một ví dụ Producer - Worker. Ý tưởng:

  • Producer sẽ gửi task vào hàng đợi (queue).
  • Worker sẽ nhận task và xử lý, giả lập công việc mất vài giây.
  • Nếu nhiều worker cùng chạy, RabbitMQ sẽ phân phối công bằng giữa chúng.

Bước 1: Khởi tạo dự án PHP

Tạo thư mục dự án, ví dụ rabbitmq-demo, rồi cài đặt PHP client cho RabbitMQ:

composer require php-amqplib/php-amqplib:^3.2

Bước 2: Tạo Producer (produce_task.php)

File produce_task.php:

Giải thích:

  • queue_declare('task_queue', ..., true, ...): queue tồn tại lâu dài.
  • delivery_mode=2: message bền bỉ (persistent).
  • Producer gửi task xong thì kết thúc ngay (không chờ xử lý).

Bước 3: Tạo Worker (worker.php)

File worker.php:

Giải thích:

  • basic_qos(null, 1, null): RabbitMQ sẽ chỉ gửi 1 task cho mỗi worker tại một thời điểm (fair dispatch).
  • Worker phải gọi basic_ack sau khi xử lý xong. Nếu worker bị crash trước khi ack, RabbitMQ sẽ gửi lại message cho worker khác.

Thực hành chạy thử

Chạy Worker

Mở 1 terminal:

php worker.php

Màn hình hiển thị:

Gửi Task với Producer

Mở terminal khác, thử gửi task:

Worker sẽ lần lượt nhận và xử lý từng task theo thời gian work_time.

Chạy nhiều Worker

Mở thêm 1–2 terminal nữa và chạy:

php worker.php

Khi đó RabbitMQ sẽ phân chia task luân phiên cho các worker — giúp tăng throughput khi hệ thống có nhiều worker.

Có thể thấy:

  • Producer gửi task ngay, không chờ xử lý → hệ thống bất đồng bộ.
  • Queue giữ lại task khi chưa có worker xử lý → như một bộ đệm (buffer).
  • Worker xử lý task và gửi ACK → tránh mất dữ liệu.
  • Nếu worker chết, RabbitMQ sẽ tự động giao lại message cho worker khác → tăng độ tin cậy.
  • Chạy nhiều worker giúp scale-out dễ dàng mà không cần thay đổi Producer.

Như vậy chúng ta đã cùng tìm hiểu qua những khai niệm cơ bản về message broker cũng như hiểu về cách triển khai của nó trong thực tế thông qua việc sử dụng RabbitMQ. Hi vọng bạn đọc thông qua bài viết này sẽ có được những thông tin hữu ích. Hẹn gặp lại mọi người vào những bài viết chất lượng sau của nhà VNLab.

Tài liệu tham khảo

  1. https://www.linkedin.com/pulse/message-queue-nguyen-trung-nam-fhnhc/
  2. https://tinasoft.io/vi/blogs/message-broker-la-gi/
  3. https://viblo.asia/p/tim-hieu-ve-rabbitmq-OeVKB8bMlkW
  4. https://topdev.vn/blog/rabbitmq-la-gi/
  5. https://aws.amazon.com/amazon-mq/
  6. https://www.rabbitmq.com/docs/install-debian
  7. https://www.rabbitmq.com/tutorials