Trả lời nhanh: Message queue (hàng đợi thông điệp) là thành phần trung gian lưu tạm các thông điệp do phần mềm này gửi để phần mềm khác lấy ra xử lý sau. Bên gửi không phải chờ bên nhận, nên hệ thống chịu được giờ cao điểm, và khi một dịch vụ lỗi thì thông điệp không bị mất.
Điểm chính
- Ba vai chính: producer (bên gửi), broker (bên giữ hàng đợi), consumer (bên nhận và xử lý).
- Độ tin cậy đến từ cơ chế xác nhận (ack), thử lại (retry) và hàng đợi thư chết (dead-letter queue).
- Hầu hết hệ thống phổ biến đảm bảo giao ít nhất một lần, nên bên nhận phải chịu được việc nhận trùng (idempotent).
- RabbitMQ, Kafka và Amazon SQS giải quyết những bài toán khác nhau, không thay thế nhau một cách máy móc.
- Hàng đợi không đáng dùng khi người dùng cần kết quả ngay trong cùng một lượt bấm.
Mục lục
- Message queue là gì?
- Message queue hoạt động như thế nào?
- Thành phần chính và các khái niệm đi kèm
- Các mô hình: hàng đợi, publish/subscribe và log
- Ví dụ minh hoạ: đợt khuyến mãi 12 giờ của shop online
- Vấn đề khó nhất: nhận trùng và quên thứ tự
- Retry, DLQ và giám sát: phần quyết định độ bền
- RabbitMQ, Kafka, Amazon SQS: chọn thế nào?
- Message queue trong hạ tầng đám mây và microservices
- Ưu điểm và nhược điểm
- Khi nào nên và không nên dùng
- Câu hỏi thường gặp
Message queue là gì?
Message queue là một dạng giao tiếp bất đồng bộ giữa các thành phần phần mềm. Thông điệp nằm trong hàng đợi cho đến khi được xử lý và xoá đi. Bên gửi chỉ cần đặt thông điệp vào hàng đợi rồi làm việc tiếp, không cần biết bên nhận đang chạy hay đang bảo trì.
Hãy hình dung quầy nhận đơn ở bưu cục. Khách bỏ gói hàng vào quầy rồi đi. Nhân viên phân loại lấy từng gói theo nhịp của mình. Nếu cuối giờ còn tồn, gói vẫn nằm đó chứ không biến mất. Thông điệp (message) ở đây thường là một gói dữ liệu nhỏ như “tạo hoá đơn cho đơn 10482” hoặc “trừ tồn kho mã SP-77”. Nó có thể ở dạng JSON, XML hoặc nhị phân.
Phần mềm đứng giữa quản lý hàng đợi gọi là message broker. Nhóm công nghệ này nằm trong khái niệm rộng hơn là phần mềm trung gian nhắn tin (messaging middleware). Nếu bạn đang tìm hiểu cách các dịch vụ tách rời nhau, hãy đọc thêm về kiến trúc microservices, nơi hàng đợi được dùng nhiều nhất.
Message queue hoạt động như thế nào?
Luồng cơ bản có ba bước. Producer gửi thông điệp tới broker. Broker lưu thông điệp trong hàng đợi. Consumer kéo (hoặc được đẩy) thông điệp về, xử lý, rồi báo xác nhận. Chỉ khi nhận xác nhận, broker mới xoá thông điệp.
Vòng đời một thông điệp khi consumer gặp sự cố
Tình huống khó nằm ở lúc consumer chết giữa chừng. Lấy Amazon SQS làm ví dụ: visibility timeout ẩn tạm thông điệp đã nhận để tránh hai consumer xử lý cùng lúc, và nếu chưa bị xoá khi hết hạn thì thông điệp hiện lại. Giá trị mặc định là 30 giây, tối đa 12 giờ. Nếu thao tác xử lý của bạn thường mất 2 phút mà để mặc định 30 giây, thông điệp sẽ hiện lại khi bản đầu vẫn đang chạy, và hệ thống xử lý đôi.
RabbitMQ đi theo cách khác: consumer xác nhận tường minh sau khi xử lý, và có giới hạn số lần giao lại. Từ RabbitMQ 4.0, quorum queue có giới hạn giao mặc định là 20 lần. Quá mức này thì thông điệp bị bỏ hoặc chuyển sang dead-letter exchange nếu đã cấu hình. Nếu chưa cấu hình nơi nhận, thông điệp biến mất lặng lẽ.
Thành phần chính và các khái niệm đi kèm
- Producer: dịch vụ tạo thông điệp, ví dụ module bán hàng sau khi chốt đơn.
- Broker: phần mềm lưu, định tuyến và giao thông điệp, như RabbitMQ, Apache Kafka, Amazon SQS.
- Queue: nơi chứa thông điệp chờ xử lý, thường theo thứ tự vào trước ra trước (FIFO).
- Consumer: dịch vụ lấy và xử lý. Có thể chạy nhiều consumer cùng đọc một hàng đợi để chia tải. Mỗi thông điệp chỉ do một consumer xử lý.
- Ack / nack: xác nhận thành công hoặc báo thất bại.
- Dead-letter queue (DLQ): hàng đợi riêng giữ các thông điệp thất bại quá số lần cho phép, để người vận hành kiểm tra. Với SQS, điều kiện chuyển là số lần nhận vượt ngưỡng cấu hình.
- Poison message: thông điệp luôn gây lỗi (ví dụ sai định dạng), nếu cứ thử lại sẽ chặn cả hàng đợi.
- Long polling: consumer chờ lâu hơn khi hàng rỗng để bớt số lần gọi vô ích. SQS cho chờ tới 20 giây.
Các mô hình: hàng đợi, publish/subscribe và log
Từ “queue” dễ gây hiểu nhầm vì có ba mô hình gần nhau.
| Tiêu chí | Hàng đợi điểm-điểm | Publish/subscribe | Log sự kiện (kiểu Kafka) |
|---|---|---|---|
| Ai nhận | Một consumer cho mỗi thông điệp | Mọi bên đăng ký đều nhận bản sao | Mỗi nhóm consumer đọc độc lập theo vị trí của mình |
| Sau khi xử lý | Thông điệp bị xoá | Tuỳ broker, thường không giữ | Dữ liệu được giữ theo thời hạn, đọc lại được |
| Hợp với | Giao việc: gửi email, tạo hoá đơn, đổi cỡ ảnh | Thông báo một sự kiện cho nhiều hệ thống | Luồng sự kiện lớn, phân tích, dựng lại trạng thái |
| Ví dụ minh hoạ | Đơn hàng chờ in phiếu giao | “Đơn đã thanh toán” gửi cho kho, kế toán, CRM | Nhật ký thao tác của từng khách để phân tích |
Một số tài liệu của IBM còn phân biệt queue với message bus: bus đòi hỏi mọi thông điệp được chuyển về cùng một kiểu, còn queue chuyển được nhiều kiểu khác nhau. Trong thực tế nhiều broker làm được cả hai mô hình đầu, nên đừng chọn theo tên gọi mà hãy chọn theo hành vi cần có.
Ví dụ minh hoạ: đợt khuyến mãi 12 giờ của shop online
Ví dụ minh hoạ (số liệu giả định): một cửa hàng mở khuyến mãi lúc 12 giờ, trong 5 phút có 10.000 đơn. Phần mềm xuất hoá đơn chỉ xử lý được khoảng 100 đơn mỗi giây. Nếu website gọi trực tiếp, trang thanh toán treo và nhiều khách thấy lỗi.
Với hàng đợi, website lưu đơn, đặt một thông điệp “xuất hoá đơn” vào queue và báo khách đã đặt thành công. Consumer xuất hoá đơn lần lượt, thêm máy khi cần. Việc này giải quyết đúng điểm nghẽn: tốc độ nhận đơn tách khỏi tốc độ xuất hoá đơn. Với bài toán hoá đơn, bạn nên đối chiếu thêm quy định hoá đơn điện tử, vì hàng đợi chỉ quyết định khi nào hoá đơn được lập, còn nội dung và thời điểm lập vẫn theo quy định hiện hành. Các mảng khác của ngành này, từ POS đến kho, được trình bày trong bài chuyển đổi số trong bán lẻ.
Vấn đề khó nhất: nhận trùng và quên thứ tự
Đa số người mới nghĩ hàng đợi đảm bảo “mỗi việc đúng một lần”. Thực tế khác. RabbitMQ, Kafka và SQS đều đảm bảo giao ít nhất một lần, nghĩa là có thể giao nhiều hơn một lần khi có sự cố. Lý do: broker không phân biệt được “consumer chưa nhận” với “consumer đã nhận nhưng báo ack bị mất”, nên cách an toàn duy nhất là giao lại.
Hệ quả cho nghiệp vụ: nếu thông điệp “trừ 1 sản phẩm khỏi kho” bị giao hai lần, kho sai. Giải pháp là thiết kế consumer có tính idempotent, tức xử lý lại cùng thông điệp thì kết quả không đổi. Cách làm phổ biến:
- Gắn mỗi thông điệp một khoá nghiệp vụ, ví dụ mã đơn cộng loại thao tác.
- Trước khi xử lý, kiểm tra khoá trong bảng đã xử lý (cơ sở dữ liệu hoặc bộ nhớ đệm như Redis).
- Nếu đã có thì bỏ qua và ack. Nếu chưa thì xử lý, ghi khoá, rồi ack.
- Đặt thời hạn lưu khoá dài hơn cửa sổ giao lại tối đa. Ví dụ với SQS, cửa sổ này xấp xỉ số lần nhận tối đa nhân visibility timeout.
Về thứ tự, SQS Standard có thể giao sai thứ tự hoặc trùng, còn hàng đợi FIFO của SQS có ràng buộc riêng. Nếu nghiệp vụ cần thứ tự tuyệt đối (ví dụ “tạo đơn” phải đến trước “huỷ đơn”), hãy chọn loại có bảo đảm thứ tự theo nhóm hoặc phân vùng, và đưa cùng mã đơn vào cùng nhóm.
Retry, DLQ và giám sát: phần quyết định độ bền
Lỗi tạm thời (mạng chập chờn, API bên thứ ba quá tải) nên thử lại với khoảng chờ tăng dần. Lỗi vĩnh viễn (dữ liệu sai) thì thử lại vô ích. Cần phân loại để đưa lỗi vĩnh viễn thẳng vào DLQ, tránh poison message chặn cả hàng.
Có hai lưu ý cấu hình hay bị bỏ sót. Thứ nhất, bật DLQ mà không đặt cảnh báo thì thông điệp chỉ chuyển chỗ nằm. Nên cảnh báo khi có thông điệp mới vào DLQ, theo tuổi và tốc độ tăng. Thứ hai, với RabbitMQ, nếu vượt giới hạn giao mà không cấu hình DLX thì thông điệp bị bỏ. Chỉ số cần theo dõi thường xuyên gồm độ dài hàng đợi, tuổi của thông điệp cũ nhất và số lần giao lại.
Thông điệp thường chứa dữ liệu thật của khách hàng như tên, số điện thoại, địa chỉ. Hàng đợi và DLQ lưu các dữ liệu này tạm thời nên cần được kiểm soát chặt. Hãy hạn chế trường dữ liệu đưa vào thông điệp, đặt thời hạn lưu và phân quyền truy cập DLQ.
RabbitMQ, Kafka, Amazon SQS: chọn thế nào?
| Tiêu chí | RabbitMQ | Apache Kafka | Amazon SQS |
|---|---|---|---|
| Bản chất | Broker nhắn tin với exchange, binding, queue | Nền tảng log sự kiện phân vùng | Dịch vụ hàng đợi do AWS quản lý |
| Điểm mạnh | Định tuyến linh hoạt, TTL, độ ưu tiên theo thông điệp | Thông lượng lớn, đọc lại sự kiện cũ | Không phải tự vận hành máy chủ |
| Cơ chế thử lại | Ack tường minh, giới hạn giao, DLX | Do ứng dụng tự quản lý vị trí đọc và DLQ | Visibility timeout và chính sách chuyển DLQ theo số lần nhận |
| Việc vận hành | Tự cài, tự theo dõi cụm | Tự vận hành hoặc dùng dịch vụ quản lý | Trả theo dịch vụ, ít cấu hình hạ tầng |
| Hợp với | Giao việc nhiều loại, nhiều quy tắc định tuyến | Luồng sự kiện lớn, nhiều bên đọc | Hệ thống đã chạy trên AWS, cần đơn giản |
Nội dung các ô trên tổng hợp từ tài liệu và bài kỹ thuật công khai; một đánh giá nêu RabbitMQ hợp định tuyến tác vụ phức tạp và hàng ưu tiên, còn Kafka mạnh ở luồng sự kiện thông lượng cao có thể phát lại. Một điểm cần nhớ khi cân nhắc chuyển đổi: Kafka không phải bản thay thế trực tiếp cho hàng đợi, vì các khái niệm như visibility timeout hay DLQ của SQS phải được viết lại trong mã ứng dụng.
Ma trận chọn nhanh
Message queue trong hạ tầng đám mây và microservices
Các nhà cung cấp đám mây đều có dịch vụ hàng đợi quản lý sẵn, nên doanh nghiệp không phải tự cài và vá lỗi broker. Đổi lại, bạn phụ thuộc vào đặc tính của dịch vụ đó, như quy tắc giao lặp hay giới hạn kích thước thông điệp. Nếu muốn nắm bối cảnh mô hình dịch vụ, xem bài điện toán đám mây.
Trong kiến trúc serverless, hàng đợi thường là bộ kích hoạt: có thông điệp mới thì một hàm được chạy, và hệ thống tự mở rộng theo độ dài hàng. Ở chiều ngược lại, hàng đợi khác với API và webhook. API và webhook gọi trực tiếp tới đối tác và thất bại nếu đối tác đang tắt. Hàng đợi đứng giữa để giữ thông điệp và thử lại, nên thường được đặt phía sau điểm nhận webhook.
Ưu điểm và nhược điểm
| Ưu điểm | Nhược điểm |
|---|---|
| Tách các dịch vụ, mỗi bên lỗi hay bảo trì không kéo bên kia | Thêm một thành phần phải vận hành, giám sát, sao lưu |
| San phẳng đỉnh tải, tăng consumer khi cần | Kết quả không có ngay, giao diện phải xử lý trạng thái “đang xử lý” |
| Thông điệp được giữ khi bên nhận gặp sự cố | Nhận trùng và sai thứ tự là chuyện bình thường, phải thiết kế để chịu được |
| Dễ chia việc cho nhiều worker | Khó gỡ lỗi hơn vì một giao dịch đi qua nhiều bước |
Phần nhược điểm cũng đã được nêu trong các thảo luận kỹ thuật: hệ thống phức tạp hơn và hai phía phải thống nhất định dạng thông điệp.
Khi nào nên và không nên dùng
Nên dùng khi:
- Việc có thể trễ vài giây đến vài phút mà người dùng không bị ảnh hưởng: gửi email, xuất hoá đơn, tạo báo cáo, đồng bộ tồn kho giữa website và kho.
- Hệ thống nhận việc nhanh hơn khả năng xử lý trong từng đợt, như khuyến mãi, chốt sổ cuối tháng.
- Một sự kiện cần báo cho nhiều hệ thống, như đơn thanh toán xong cần kho, kế toán và chăm sóc khách hàng cùng biết.
Chưa nên dùng khi:
- Người dùng cần câu trả lời ngay trong cùng lượt bấm, như kiểm tra số dư trước khi cho rút. Gọi đồng bộ phù hợp hơn.
- Ứng dụng chỉ có một khối và khối lượng nhỏ, cơ sở dữ liệu xử lý dư sức. Thêm broker chỉ tạo thêm chỗ hỏng.
- Nghiệp vụ cần giao dịch chặt chẽ qua nhiều hệ thống nhưng chưa ai thiết kế bước bù trừ khi một bước thất bại.
Nếu hệ thống chạy trên Kubernetes, consumer thường là các container có thể tăng giảm số bản sao theo độ dài hàng đợi. Điều đó hữu ích nhưng không thay được việc thiết kế consumer chịu được nhận trùng.
Câu hỏi thường gặp
Message queue khác message broker thế nào?
Queue là cấu trúc chứa thông điệp chờ xử lý. Broker là phần mềm quản lý một hoặc nhiều queue, định tuyến, xác nhận và thử lại. Trong thực tế người ta hay dùng hai từ này thay nhau.
Message queue có đảm bảo không mất thông điệp không?
Phụ thuộc cấu hình. Hàng đợi lưu bền xuống đĩa, có xác nhận từ cả hai phía và DLQ sẽ giảm nguy cơ mất. Nhưng đổi lại là khả năng giao trùng, nên consumer phải xử lý được việc nhận lại cùng một thông điệp.
Có thể dùng cơ sở dữ liệu làm hàng đợi không?
Được với tải nhỏ: một bảng công việc có cột trạng thái và một tiến trình quét. Khi khối lượng tăng, bạn phải tự xử lý khoá tranh chấp, thử lại và giám sát. Lúc đó broker chuyên dụng thường đỡ công hơn.
Message queue có phải là Kafka không?
Không hoàn toàn. Kafka là nền tảng log sự kiện, lưu dữ liệu theo thời hạn và cho đọc lại. Nó dùng được cho nhiều kiểu bài toán hàng đợi, nhưng cách vận hành và các khái niệm khác với hàng đợi truyền thống như SQS hay RabbitMQ.
Doanh nghiệp nhỏ có cần message queue không?
Chỉ khi có việc chạy nền gây chậm hoặc nghẽn thật, ví dụ đồng bộ đơn từ nhiều kênh bán hàng. Nếu phần mềm hiện tại đã đủ nhanh và ít tích hợp, chưa cần thêm. Nhiều phần mềm SaaS đã tích hợp sẵn hàng đợi bên trong, bạn không cần tự dựng.
Làm sao biết hàng đợi đang có vấn đề?
Ba dấu hiệu: độ dài hàng tăng mãi không giảm, thông điệp cũ nhất ngày càng già, và DLQ liên tục có thông điệp mới. Hãy đặt cảnh báo cho cả ba chỉ số này ngay từ đầu.
VBPO Tech Blog cập nhật tin tức và kiến thức công nghệ cho doanh nghiệp Việt Nam. Xem thêm các bài cùng chuyên mục Hạ tầng số & đám mây.