Trong các bài viết trước chúng ta đã tìm hiểu về JMS và ActiveMQ. Trong bài này, chúng ta sẽ tìm hiểu một message broker khác là RabbitMQ.
Nội dung [Ẩn]
RabbitMQ là gì?
RabbitMQ là một message Broker (MOM – Message-Oriented Middleware), sử dụng giao thức AMQP (Advanced Message Queue Protocol).
RabbitMQ được viết bằng ngôn ngữ Erlang.
RabbitMQ là một phần mềm trung gian được sử dụng để trao đổi dữ liệu giữa các process, application, system hoặc server. RabbitMQ sẽ 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.
RabbitMQ chạy trên nhiều OS, Cloud và cung cấp một loạt các công cụ dành cho nhà phát triển với hầu hết các ngôn ngữ phổ biến.
RabbitMQ không phải là một JMS Provider, nhưng nó cung cấp plugin cần thiết hỗ trợ mô hình JMS Queue và JMS Topic.
Tại sao sử dụng RabbitMQ?
Vấn đề
Giả sử chúng ta có một web application cho phép user đăng ký thông tin. Sau khi đăng ký, hệ thống sẽ xử lý thông tin, generate PDF và gửi email lại user. Những công việc này hệ thống cần nhiều thời gian để xử lý, chẳng hạn. Nếu xử lý theo cách thông thường thì user sẽ phải chờ đến khi hệ thống xử lý hoàn thành, và nếu hệ thống có hàng nghìn user truy cập cùng lúc sẽ gây quá tải server.
Đối với những loại công việc thế này, chúng ta sẽ xử lý nó bằng cách sử dụng message queue: Sau khi user nhập đầy đủ các thông tin trên web interface, web application sẽ tạo một Message “Generate PDF” chứa đầy đủ thông tin cần thiết và gửi nó vào Queue của RabbitMQ.
Đây là một architecture cơ bản của message queue.
- Producer : là ứng dụng client, tạo message và publish tới broker.
- Consumer : là ứng dụng client khác, kết nối đến queue, subscribe (đăng ký) và xử lý (consume) message.
- Broker (RabbitMQ) : nhận message từ Producer, lưu trữ chúng an toàn trước khi được lấy từ Consumer.
Flow đầy đủ sử dụng RabbitMQ để giải quyết vấn đề trên:
- (1) User gửi yêu cầu tạo PDF đến web application.
- (2) Web application (Producer) gửi tin nhắn đến RabbitMQ bao gồm dữ liệu từ request như tên và email.
- (3) Một Exchange chấp nhận các tin nhắn từ Producer và định tuyến chúng đến Queue (hàng đợi) để tạo PDF.
- (4) Ứng dụng xử lý PDF (Consumer) nhận Message từ Queue và bắt đầu xử lý PDF.
Một số vấn đề khác:
- Đối với các hệ thống sử dụng kiến trúc microservice thì việc gọi chéo giữa các service khá nhiều, khiến cho luồng xử lý khá phức tạp.
- Mức độ trao đổi data giữa các thành phần tăng lên khiến cho việc lập trình trở nên khó khăn hơn (vấn đề maintain khá đau đầu).
- Khi phát triển ứng dụng làm sao để các lập trình viên tập trung vào các domain, business logic thay vì các công việc trao đổi ở tầng infrastructure.
- Với các hệ thống phân tán, khi việc giao tiếp giữa các thành phần với nhau đòi hỏi chúng cần phải biết nhau. Nhưng điều này gây rắc rối cho việc viết code. Một thành phần phải biết quá nhiều dẫn đến rất khó maintain, debug.
Lợi ích và tính năng của RabbitMQ
- Transparency: Một producer không cần phải biết consumer. Nó chỉ việc gửi message đến các queue trong message broker. Consumer chỉ việc đăng ký nhận message từ các queue này.
- Many Client: Vì producer giao tiếp với consumer trung gian qua message broker nên dù producer và consumer có khác biệt nhau về ngôn ngữ thì giao tiếp vẫn thành công. Hiện nay rabbitmq đã hỗ trợ rất nhiều ngôn ngữ khác nhau.
- Asynchronous (bất đồng bộ): Producer không thể biết khi nào message đến được consumer hay khi nào message được consumer xử lý xong. Đối với producer, đẩy message đến message broker là xong việc. Consumer sẽ lấy message về khi nó muốn. Đặc tính này có thể được tận dụng để xây dựng các hệ thống lưu trữ và xử lý log.
- Flexible Routing: message được định tuyến (route) thông qua Exchange trước khi đến Queue. RabbitMQ cung cấp một số loại Exchange thường dùng, chúng ta cũng có thể định nghĩa riêng Exhange cho riêng mình.
- Lightweight: một single instance của RabbitMQ chỉ chiếm khoảng 40MB RAM.
- Multiple message protocol: AMQP, MQTT, STOMP, HTTP.
- Cluster: các bạn có thể gom nhiều rabbitmq instance vào một cluster. Một queue được đinh nghĩa trên một instance khi đó đều có thể truy xuất từ các instance còn lại. Có thể tận dụng để làm load balancing.
- High availibilty: cho phép failover khi sử dụng mirror queue.
- Reliability: có cơ chế ACK để đảm bảo message được nhận bởi consumer đã được xử lý, lưu trữ (persistence) message, .high availability, publisher confirm, …
- Extensibility: cung cấp hệ thống plugin linh hoạt, dễ dàng tích hợp các plugin của third party. Ví dụ: plugin lưu message vào cơ sở dữ liệu.
- Cloud: dễ dàng triển khai với hạ tầng hiện có hoặc Cloud.
- Management & Monitoring: cung cấp HTTP API, command-line tool và UI để quản lý và giám sát.
- Tools support: Hoạt động với các công cụ CI/CD và có thể triển khai với BOSH, Chef, Docker và Puppet.
Những khái niệm cơ bản trong RabbitMQ
- Producer: Ứng dụng gửi message.
- Consumer: Ứng dụng nhận message.
- Queue: Lưu trữ messages.
- Message: Thông tin truyền từ Producer đến Consumer qua RabbitMQ.
- Connection: Một kết nối TCP giữa ứng dụng và RabbitMQ broker.
- Channel: Một kết nối ảo trong một Connection. Việc publishing hoặc consuming message từ một queue đều được thực hiện trên channel.
- Exchange: Là nơi nhận message được publish từ Producer và đẩy chúng vào queue dựa vào quy tắc của từng loại Exchange. Để nhận được message, queue phải được nằm (binding) trong ít nhất 1 Exchange.
- Binding: là quy tắc (rule) mà Exchange sử dụng để định tuyến Message đến Queue. Đảm nhận nhiệm vụ liên kết giữa Exchange và Queue.
- Routing key: Một key mà Exchange dựa vào đó để quyết định cách để định tuyến message đến queue. Có thể hiểu nôm na, Routing key là địa chỉ dành cho message.
- AMQP (Advance Message Queuing Protocol): là giao thức truyền message được sử dụng trong RabbitMQ.
- User: Để có thể truy cập vào RabbitMQ, chúng ta phải có username và password. Trong RabbitMQ, mỗi user được chỉ định với một quyền hạn nào đó. User có thể được phân quyền đặc biệt cho một Vhost nào đó.
- Virtual host/ Vhost: Cung cấp những cách riêng biệt để các ứng dụng dùng chung một RabbitMQ instance. Những user khác nhau có thể có các quyền khác nhau đối với vhost khác nhau. Queue và Exchange có thể được tạo, vì vậy chúng chỉ tồn tại trong một vhost.
RabbitMQ hoạt động như thế nào?
- (1) Producer đẩy message vào Exchange. Khi tạo Exchange, bạn phải mô tả nó thuộc loại gì. Các loại Exchange sẽ được giải thích phía dưới.
- (2) Sau khi Exchange nhận Message, nó chịu trách nhiệm định tuyến message. Exchange sẽ chịu trách nhiệm về các thuộc tính của Message, ví dụ routing key, loại Exchange.
- (3) Việc binding phải được tạo từ Exchange đến Queue (hàng đợi). Trong trường hợp này, ta sẽ có hai binding đến hai hàng đợi khác nhau từ một Exchange. Exchange sẽ định tuyến Message vào các hàng đợi dựa trên thuộc tính của của từng Message.
- (4) Các Message nằm ở hàng đợi đến khi chúng được xử lý bởi một Consumer.
- (5) Consumer xử lý Message nhận từ Queue.
Exchange và các loại Exchange
Message không được publish trực tiếp vào Queue; thay vào đó, Producer gửi message đến Exchange.
Exchange là nơi mà các message được gởi. Exchange nhận tin nhắn và định tuyến nó đến 0 hoặc nhiều Queue với sự trợ giúp của các ràng buộc (binding) và các khóa định tuyến (routing key).
Thuật toán định tuyến được sử dụng phụ thuộc vào loại Exchange và quy tắc (còn gọi là ràng buộc hay binding).
Có 4 loại Exchange: Direct, Fanout, Topic, Headers. Lựa chọn các exchange type khác nhau sẽ dẫn đến các đối xử khác nhau của message broker với tin nhắn nhận được từ producer. Exchange được bind (liên kêt) đến một số Queue nhất định.
Exchange type | Tên mặc định |
---|---|
Direct exchange | (Empty string) hoặc amq.direct |
Fanout exchange | amq.fanout |
Topic exchange | amq.topic |
Headers exchange | amq.match (và amq.headers trong RabbitMQ) |
Ngoài Exchange type, Exchange còn định nghĩa một số thuộc tính:
- Name: tên Exchange.
- Durability: thời gian tồn tại khi broker restart
- Auto-delete: exchange bị xóa khi hàng đợi cuối cùng không còn binding từ nó.
- Arguments: các tham số không bắt buộc, được sử dụng bởi các plugin và các tính năng dành riêng cho broker.
Direct Exchange
Direct Exchange (trao đổi trực tiếp) định tuyến message đến Queue dựa vào routing key. Thường được sử dụng cho việc định tuyến tin nhắn unicast-đơn hướng (mặc dù nó có thể sử dụng cho định tuyến multicast-đa hướng). Các bước định tuyến message:
- Một queue được ràng buộc với một direct exchange bởi một routing key K.
- Khi có một message mới với routing key R đến direct exchange. Message sẽ được chuyển tới queue đó nếu R=K.
Một Exchange không xác định tên (empty ttring), đây là loại Default Exchange, một dạng đặc biệt của là Direct Exchange. Default Exchange được liên kết ngầm định với mọi Queue với khóa định tuyến bằng với tên Queue.
Direct Exchange hữu ích khi muốn phân biệt các thông báo được publish cho cùng một exchange bằng cách sử dụng một mã định danh chuỗi đơn giản.
Ví dụ, nếu hàng đợi (Queue) gắn với một exchange có binding key là pdf_create, message được đẩy vào exchange với routing key là pdf_create sẽ được đưa vào hàng đợi này.
Fanout Exchange
Fanout exchange định tuyến message (copy message) tới tất cả queue mà nó được bind, với bất kể một routing key nào. Giả sử, nếu nó N queue được bind bởi một Fanout exchange, khi một message mới published, exchange sẽ định tuyến message đó tới tất cả N queues. Fanout exchange được sử dụng cho định tuyến message broadcast (quảng bá).
Exchange Fanout hữu ích với trường hợp ta cần một dữ liệu được gửi tới nhiều ứng dụng khác nhau với cùng một message nhưng cách xử lý ở ứng dụng là khác nhau.
Topic Exchange (Publish/Subscribe)
Topic exchange định tuyến message tới một hoặc nhiều queue dựa trên sự trùng khớp giữa routing key và pattern. Topic exchange thường sử dụng để thực hiện định tuyến thông điệp multicast. Ví dụ một vài trường hợp sử dụng:
- Phân phối dữ liệu liên quan đến vị trí địa lý cụ thể.
- Xử lý tác vụ nền được thực hiện bởi nhiều workers, mỗi công việc có khả năng xử lý các nhóm tác vụ cụ thể.
- Cập nhật tin tức liên quan đến một category hoặc gắn tag.
- Điều phối các dịch vụ của các loại khác nhau trong cloud.
Một topic exchange sẽ sử dụng wildcard để gắn routing key với một routing pattern khai báo trong binding. Consumer có thể đăng ký những topic mà nó quan tâm.
Ví dụ:
- agreements.*.headstore : Được đăng ký bởi tất cả những key với pattern bắt đầu bằng agreements, theo sau là một từ bất kỳ và kết thúc là headstore.
- agreements.# : Được đăng ký bởi tất cả các key bắt đầu với agreements.
Chi tiết về các wildcard tôi sẽ giới thiệu với các bạn ở một bài viết khác.
Headers Exchange
Header exchange được thiết kế để định tuyến với nhiều thuộc tính, để dàng thực hiện dưới dạng header của message hơn là routing key. Header exchange bỏ đi routing key mà thay vào đó định tuyến dựa trên header của message. Trường hợp này, broker cần một hoặc nhiều thông tin từ application developer, cụ thể là, nên quan tâm đến những tin nhắn với tiêu đề nào phù hợp hoặc tất cả chúng.
Headers Exchange rất giống với Topic Exchange, nhưng nó định tuyến dựa trên các giá trị header thay vì routing key.
Một Message được coi là phù hợp nếu giá trị của header bằng với giá trị được chỉ định khi ràng buộc.
Dead Letter Exchange
Nếu không tìm thấy hàng đợi phù hợp cho tin nhắn, tin nhắn sẽ tự động bị hủy. RabbitMQ cung cấp một tiện ích mở rộng AMQP được gọi là “Dead Letter Exchange”. Cung cấp chức năng để chụp các tin nhắn không thể gửi được.
Trên đây là những kiến thức cơ bản về RabbitMQ. Bài viết này hơi năng về lý thuyết, nhưng rất quan trọng để hiểu và dễ dàng làm việc với RabbitMQ sau này.
Tài liệu tham khảo:
- https://www.rabbitmq.com/features.html
- https://www.rabbitmq.com/tutorials/amqp-concepts.html
- https://www.cloudamqp.com/blog/2015-05-18-part1-rabbitmq-for-beginners-what-is-rabbitmq.html
- https://www.cloudamqp.com/blog/2015-09-03-part4-rabbitmq-for-beginners-exchanges-routing-keys-bindings.html