29. tháng 1 2025
Khi nhắc đến hàng đợi tin nhắn, chắc hẳn những ai đã làm việc với phần backend của web trong một khoảng thời gian đều từng sử dụng qua. Ở công ty trước, tôi đã làm việc với phiên bản cải tiến của NSQ, giống như một phiên bản rút gọn của NOSQL 😐 Thực tế, đó là một hàng đợi tin nhắn được viết bằng ngôn ngữ Go. Mặc dù các bản cập nhật mã nguồn của NSQ không thường xuyên, nhưng nhờ có đội ngũ chuyên trách về middleware tại công ty cũ, nên nó vẫn hoạt động rất tốt. Các chuyên gia trong nhóm này cũng rất tài năng, vì chúng tôi chưa bao giờ gặp phải tình trạng mất tin nhắn nào cả.
Hiện tại, ở công ty hiện tại của tôi, chúng tôi đang sử dụng RocketMQ. Với tư cách là một người mới học, tôi nghĩ rằng việc hiểu rõ hơn về RocketMQ là điều cần thiết, đặc biệt khi hàng đợi tin nhắn là một thành phần quan trọng trong phát triển backend. Tôi tin rằng hầu hết các đội ngũ backend đều cần dùng đến hàng đợi tin nhắn. Trước đây, tôi không thực sự hiểu sâu về nguyên lý hoạt động của NSQ, vậy nên bây giờ tôi muốn tìm hiểu thêm về RocketMQ.
Để giải thích cho những người mới như tôi, hàng đợi tin nhắn được sử dụng để làm gì? Nhiều câu trả lời tiêu chuẩn sẽ nói rằng nó giúp "giảm tải và cân bằng khối lượng công việc". Điều này hoàn toàn đúng, nhưng tôi muốn giải thích theo một kịch bản cụ thể dành cho những bạn cũng là người mới như tôi.
Hãy tưởng tượng một chức năng đặt hàng trên nền tảng thương mại điện tử. Sau khi thanh toán hoàn tất (hoặc thất bại hoặc bị timeout), hệ thống sẽ nhận được phản hồi từ cổng thanh toán. Trong phản hồi này, chúng ta cần thực hiện nhiều tác vụ khác nhau: thay đổi trạng thái đơn hàng, xác nhận phiếu giảm giá, hủy các khoản tiền liên quan, xóa sản phẩm khỏi giỏ hàng, gửi thông báo chăm sóc khách hàng, cập nhật điểm thưởng cho người dùng, v.v.
Nếu tất cả các thao tác này được xử lý trực tiếp trong phản hồi thanh toán, thì yêu cầu về độ ổn định của mã nguồn và hiệu suất của dịch vụ liên quan sẽ cực kỳ cao để tránh lỗi nghiệp vụ. Hơn nữa, nếu lưu lượng truy cập tăng đột biến, các tác vụ quan trọng và không quan trọng sẽ bị chặn lại cùng nhau. Vì vậy, chúng ta cần sử dụng một hàng đợi tin nhắn. Khi nhận được phản hồi thanh toán, chúng ta chỉ xử lý vài thao tác cốt lõi rồi đẩy tin nhắn vào hàng đợi. Sau đó, các bên liên quan sẽ tiêu thụ tin nhắn này để thực hiện các nhiệm vụ phụ như gửi thông báo chăm sóc khách hàng, cập nhật điểm thưởng, v.v.
Ví dụ này không phải là một ví dụ điển hình nhất về việc "giảm tải và cân bằng khối lượng công việc", nhưng nó vẫn là một trường hợp sử dụng phổ biến của hàng đợi tin nhắn. Để hiểu rõ hơn về việc giảm tải, hãy tưởng tượng rằng có 10.000 yêu cầu tới hệ thống trong một khoảng thời gian ngắn, trong khi hệ thống chỉ hỗ trợ tối đa 1.000 QPS. Trong trường hợp bình thường, hệ thống sẽ ngừng hoạt động hoặc bị giới hạn lưu lượng. Để đảm bảo hệ thống hoạt động bình thường, chúng ta có thể đẩy tất cả các yêu cầu này vào hàng đợi tin nhắn và tiêu thụ chúng theo khả năng xử lý của máy chủ.
Bây giờ, hãy cùng tìm hiểu RocketMQ trông như thế nào nhé!
RocketMQ bao gồm bốn thành phần chính: NameServer, Broker, Producer, Consumer.
NameServer đóng vai trò như một trung tâm đăng ký đơn giản cho các Topic. Nó tương tự như Zookeeper trong Dubbo, hỗ trợ đăng ký và phát hiện động các Broker. NameServer có hai chức năng chính:
NameServer thường được triển khai dưới dạng cụm, các instance không giao tiếp thông tin lẫn nhau. Mỗi Broker đăng ký thông tin định tuyến của mình lên mọi NameServer, vì vậy mỗi instance NameServer đều có một bản sao đầy đủ của thông tin định tuyến. Nếu một NameServer dừng hoạt động do bất kỳ lý do nào, các Broker vẫn có thể đồng bộ thông tin định tuyến lên các NameServer bóng đá trực tiếp khác, và Producer, Consumer vẫn có thể cảm nhận được thông tin định tuyến của Broker một cách động.
Áp lực lên NameServer không quá lớn, chủ yếu chịu trách nhiệm duy trì nhịp tim và cung cấp dữ liệu mối quan hệ giữa Topic và Broker. Tuy nhiên, cần lưu ý rằng khi Broker gửi nhịp tim đến NameServer, nó sẽ mang theo thông tin của tất cả các Topic mà nó quản lý. Nếu số lượng Topic quá lớn, kích thước dữ liệu trong mỗi nhịp tim có thể trở nên rất lớn, dẫn đến thất bại truyền mạng và kết quả là NameServer có thể nhầm lẫn rằng Broker đã thất bại trong việc gửi nhịp tim.
Broker chịu trách nhiệm chính về lưu trữ, chuyển giao và truy vấn tin nhắn cũng như đảm bảo tính sẵn sàng cao của dịch vụ. Để thực hiện các chức năng này, Broker bao gồm các mô-đun con quan trọng sau:
Cân bằng tải: Thông tin Topic được lưu trữ trên Broker, Topic được chia thành nhiều hàng đợi và phân tán đều trên các Broker khác nhau. Cơ chế gửi của Producer đảm bảo rằng các tin nhắn được phân bổ đều đến tất cả các hàng đợi, giúp đạt được hiệu quả cân bằng tải cho toàn bộ tin nhắn trên mỗi Broker.
Khả năng mở rộng động (cho tin nhắn không theo thứ tự): Khả năng mở rộng của Broker thể hiện ở hai chiều kích: Topic và Broker.
Tính sẵn sàng cao & đáng tin cậy cao:
Producer thiết lập kết nối dài hạn với một node bất kỳ trong cụm NameServer (được chọn ngẫu nhiên), định kỳ lấy thông tin định tuyến Topic từ NameServer và thiết lập kết nối dài hạn với Master cung cấp dịch vụ Topic. Producer hoàn toàn vô trạng thái và có thể triển khai dưới dạng cụm. RocketMQ cung cấp ba phương thức gửi tin:
Khi gửi tin nhắn, Producer sẽ tự động luân phiên qua tất cả các Broker có sẵn để gửi tin nhắn. Mỗi tin nhắn được gửi thành công, Producer sẽ chuyển sang Broker khác để gửi tin nhắn tiếp theo, đảm bảo rằng các tin nhắn được phân bổ đều trên tất cả các Broker.
Consumer thiết lập kết nối dài hạn với một node bất kỳ trong cụm NameServer (được chọn ngẫu nhiên), định kỳ lấy thông tin định tuyến Topic từ NameServer và thiết lập kết nối dài hạn với cả Master và Slave cung cấp dịch vụ Topic. Consumer có thể đăng ký tin nhắn từ Master hoặc Slave. Khi Consumer kéo tin nhắn từ Master, máy chủ Master sẽ gợi ý cho lần kéo tiếp theo dựa trên khoảng cách giữa vị trí kéo hiện tại và vị trí lớn nhất, cũng như khả năng đọc từ Slave.
Trước tiên, hãy thảo luận về các chế độ tiêu thụ của Consumer:
Cân bằng tải ở phía Consumer, trong chế độ tiêu thụ theo cụm, đảm bảo rằng tất cả các instance Consumer có cùng ID sẽ tiêu thụ đều các hàng đợi của Topic.
Loại Consumer từ góc độ người dùng:
####补充一些概念 Topic: Là tập hợp các tin nhắn thuộc cùng một loại, mỗi Topic chứa nhiều tin nhắn và mỗi tin nhắn chỉ thuộc một Topic duy nhất. Đây là đơn vị cơ bản để đăng ký tin nhắn trong RocketMQ. Topic có mối quan hệ rất lỏng lẻo với Producer và Consumer. Một Topic có thể có 0 hoặc nhiều Producer gửi tin nhắn, và một Producer có thể gửi tin nhắn đến nhiều Topic khác nhau. Từ góc độ Consumer, một Topic có thể được đăng ký bởi 0 hoặc nhiều nhóm Consumer, và một nhóm Consumer có thể đăng ký một hoặc nhiều Topic miễn là các instance trong nhóm giữ nguyên đăng ký của họ.
Message: Là đơn vị nhỏ nhất của dữ liệu được sản xuất và tiêu thụ trong hệ thống tin nhắn. Mỗi tin nhắn phải thuộc về một Topic. Trong RocketMQ, mỗi tin nhắn có một Message ID duy nhất và có thể mang theo một Key có ý nghĩa nghiệp vụ. Hệ thống cung cấp chức năng truy vấn tin nhắn qua Message ID và Key.
Message Queue: Một Topic có thể được chia thành một hoặc nhiều sub-topics ("hàng đợi tin nhắn").
Tag: Là nhãn được gắn cho tin nhắn, giúp phân biệt các loại tin nhắn khác nhau trong cùng một Topic. Tin nhắn từ cùng một đơn vị nghiệp vụ có thể được gắn nhãn khác nhau tùy theo mục đích nghiệp vụ. Tag giúp giữ cho mã nguồn sạch sẽ và mạch lạc, đồng thời tối ưu hóa hệ thống truy vấn của RocketMQ. Consumer có thể sử dụng Tag để thực hiện các logic tiêu thụ khác nhau cho các sub-topics khác nhau, tăng cường khả năng mở rộng.
Message Order: Khi sử dụng DefaultMQPushConsumer, bạn cần xác định cách tiêu thụ tin nhắn:
Consumer Group: Là tập hợp các Consumer cùng loại, thường tiêu thụ cùng loại tin nhắn và có cùng logic tiêu thụ. Nhóm Consumer giúp dễ dàng đạt được mục tiêu cân bằng tải và dung sai trong việc tiêu thụ tin nhắn. Lưu ý rằng tất cả các instance Consumer trong cùng một nhóm phải đăng ký cùng các Topic.
Producer Group: Là tập hợp các Producer cùng loại, thường gửi cùng loại tin nhắn và có cùng logic gửi. Nếu tin nhắn là giao dịch và Producer gốc bị sập sau khi gửi, máy chủ Broker sẽ liên lạc với các instance Producer khác trong cùng một nhóm để cam kết hoặc hoàn tác tiêu thụ.
Nội dung trên chủ yếu dựa trên tài liệu GitHub của RocketMQ và một số bài viết chất lượng từ cộng đồng mạng. Nếu có vi phạm bản quyền, vui lòng liên hệ để xóa bỏ.