Tổng quan
RabbitMQ là một phần mềm trung gian cung cấp dịch vụ triển khai và quản lý message queue, hay còn được gọi là message broker. RabbitMQ hỗ trợ các giao thức thường sử dụng bao gồm AMQP 1.0 và MQTT 5. Nó cung cấp nhiều thư viện và hầu hết các ngôn ngữ lập trình đều hỗ trợ sử dụng RabbitMQ. RabbitMQ cung cấp khả năng dễ dàng triển khai trên nhiều môi trường và cung cấp nhiều chức năng đảm bảo tính ổn định cho hệ thống message broker.
Phân tích chuyên sâu
Ưu và nhược điểm khi sử dụng Message Broker
Ưu điểm
- Tách một hệ thống lớn ra thành nhiều phần nhỏ hơn giúp dễ kiểm soát hơn
- Xử lý các tác vụ chạy ngầm không đồng bộ giúp giảm thời gian phản hồi của hệ thống
- Là một cách để giải quyết bài toán lượng truy cập trong 1 thời điểm bất ngờ tăng cao vượt qua ngưỡng chịu tải của hệ thống ➔ Bằng cách đưa các request cần xử lý vào trong queue và xử lý từ từ
Nhược điểm
- Tăng độ phức tạp cho cấu trúc hệ thống
- Có khả năng bị mất message
- Cần phải xử lý đảm bảo tính đồng bộ giữa các thành phần trong hệ thống
Các khái niệm cốt lõi
RabbitMQ bao gồm 3 khái niệm cốt lõi vận hành nên hệ thống là Producer, Queue và Consumer.
- Producer là thành phần chịu trách nhiệm gửi các message. Producer chỉ quan tâm đến việc gửi message với các thông tin cần thiết đi mà không cần quan tâm đền việc message sẽ được xử lý như thế nào
- Queue: Là nơi lưu trữ các message chờ để xử lý
- Consumer: Là thành phần sẽ chịu trách nhiệm xử lý các tác vụ của message.
Exchange
Trong RabbitMQ thì Producer không gửi trực tiếp message vào queue mà gửi thông qua thành phần trung gian là exchange. Thành phần này sẽ có mục đích phân loại các message nào sẽ được đưa vào queue nào để xử lý tùy thuộc theo quy định được định nghĩa của exchange dựa trên type của exchange đó.
Có 4 loại exchange chính là Direct exchange, Fanout Exchange, Topic Exchange, Header Exchange
Direct exchange
- Là phương thức đưa message trực tiếp đến queue dựa trên routing key của message. Cách thức hoạt động của nó là khi producer gửi message tới Direct Exchange thì Direct Exchange sẽ dựa trên routing key của message đó mà gửi đến queue tương ứng dựa trên routing key của binding mà queue đó liên kết với exchange
- Default exchange: cũng thuộc về Direct exchange với cách hoạt động tương tự nhưng có điểm đặc biệt là không có routing key (routing key là một chuỗi rỗng). Exchange này ngầm định liên kết với mọi queue với khóa định tuyến là tên của queue
Fanout Exchange
- Là phương thức đưa message tới mọi queue được binding với exchange mà không cần có thêm bất kì kiểm tra nào. Cách thức hoạt động của nó là các queue sẽ binding vào exchange mà không cần routing key. Khi có một message mới thì exchange này sẽ truyền vào tất cả các key được binding với nó
- Fanout Exchange thường được ứng dụng vào broadcast message hoặc trong trường hợp nhiều consumer đều cần nhận được thông báo (message) cùng một lúc.
Topic Exchange
- Topic Exchange: Là phương thức đưa message tới một hoặc nhiều queue dựa trên sự hợp lệ trùng khớp giữa routing key và pattern được quy định. Cách thức hoạt động của nó là các queue sẽ binding với Topic Exchange với các routing pattern được định nghĩa trên các binding đó. Khi có một message mới thì Topic Exchange sẽ dựa trên routing key của message mà so sánh xem trùng khớp với pattern nào và sẽ gửi message đó đến queue tương ứng. Queue được chọn có thể có 1 hoặc nhiều queue chỉ cần trùng khớp.
- Các kí tự được sử dụng trong pattern:
.
: dấu phân cách giữa các từ*
: đại diện cho 1 từ bất kì. VD: topic.* sẽ match với topic.music, topic.word, ... mà không match với topic.word.name#
: đại diện cho 0 hoặc nhiều từ bất kì. VD: topic.# sẽ match với topic.music.top, topic.movie.top, ... mà không match với movie.topic.name
Header Exchange
- Là phương thức thay vị định tuyến dựa trên routing key của message thì sẽ sử dụng header của message để định tuyến. Ưu điểm của phương thức này là có thể định tuyến với nhiều thông tin phức tạp hơn được đưa ở trên header.
- Header Exchange cũng có thể gửi message đến 1 hoặc nhiều queue không giới hạn số lượng giống với Topic Exchange
Message Acknowledgment
- Trong khi xử lý message thì một vấn đề đặt ra là khi có một tác vụ có thời gian quá dài thì đôi khi có trường hợp consumer sẽ bị kết thúc trước khi tác vụ đó hoàn thành và không thể kiểm soát được là tác vụ đó đã hoàn thành hay chưa, dẫn đến khả năng bị miss message. Vì vậy RabbitMQ cung cấp một cơ chế là Message Acknowledgment để giải quyết vấn đề này.
- Cơ chế này hoạt động theo cách khi một message được xử lý xong thì sẽ gửi về một message acknowledgment để báo cho RabbitMQ biết là đã xử lý xong message đó, nếu trong trường hợp message đó đang xử lý mà có vấn đề xảy ra như là channel bị đóng, kết nối bị ngắt, ... và không thể gửi message acknowledgment về được thì RabbitMQ sẽ xem như message đó chưa được xử lý và có thể đưa message đó vào lại queue để xử lý tiếp ➔ Từ đó đảm bảo được rằng sẽ không có message nào bị mất không xử lý
- Nếu client (consumer) sau khi nhận message và xử lý xong nhưng gửi ack về là false thì message đó sẽ được đánh dấu là unacked ➔ nghĩa là message này chưa được xử lý và sẽ gửi tiếp cho các consumer khác
Các vấn đề liên quan đến durability của message
Time To Live (TTL)
- Trong RabbitMQ có hỗ trợ có thể config TTL cho message. Nghĩa là khi đến thời gian TTL thì message sẽ tự động được xóa đi khỏi hệ thống RabbitMQ dù là có được xử lý hay chưa
- Tính năng này giúp cho chúng ta có thể kiểm soát được thời gian tồn tại của message, đảm bảo không có message nào tồn tại quá lâu trong queue một cách không cần thiết gây lãng phí tài nguyên
- Lưu ý khi sử dụng tính năng này là phải xác định được message đó có thể xóa sau một thời gian được hay không dựa trên độ quan trọng của message. Nếu có các message có data quan trọng thì lưu ý khi sử dụng vì có thây gây mất data vĩnh viễn
Message Durable
- Với cơ chế Ack thì RabbitMQ đã có thể giải quyết vấn đề không bị mất message khi consumer bị lỗi, tuy nhiên nếu trường hợp server bị sập thì tất cả các message trong queue đều sẽ bị mất ➔ Gây một rủi ro lớn về mất dữ liệu khi sử dụng RabbitMQ. Vì vậy RabbitMQ đã cung cấp tính năng durable để xử lý vấn đề này
- Khi thiết lập Durable = true cho queue thì có nghĩa khi có vấn đề nào đó xảy ra dẫn đến sever bị sập hoặc mất kết nối hoặc services RabbitMQ phải khởi động lại thì các message trong queue vẫn sẽ được lưu giữ lại cho lần khởi chạy tiếp theo
- Metadata của durable queue sẽ được lưu trên ổ đĩa, do đó khi khởi động lại thì message sẽ được phục hồi và không bị mất dữ liệu
- Lưu ý: Với durable queue thì producer vẫn phải đánh dấu message gửi lên có persisted = true. Vì nếu như không đánh dấu message như vậy thì khi khởi động lại thì message đó vẫn sẽ bị mất dù có được đưa vào durable queue.