Mở đầu — vì sao bài này quan trọng
Trong bài trước, chúng ta đã có cái nhìn tổng quan về message broker — vì sao hệ thống hiện đại cần một lớp trung gian để các service nói chuyện với nhau mà không bị ràng buộc chặt. Nhưng ngay khi bạn ngồi vào bàn thiết kế thật, câu hỏi đầu tiên và cũng khó nhất luôn xuất hiện: "Chúng ta nên dùng Kafka hay RabbitMQ?"
Đây không phải câu hỏi thời thượng. Đây là một quyết định kiến trúc có hậu quả lâu dài, thường kéo theo hàng năm trời chi phí vận hành, tuyển người, và nợ kỹ thuật. Tôi đã chứng kiến nhiều đội chọn sai — đội thì kéo Kafka vào chỉ để gửi vài trăm email mỗi ngày rồi khổ sở với việc vận hành ba node ZooKeeper, đội thì cố nhồi một luồng dữ liệu real-time hàng triệu event/giây qua RabbitMQ rồi ngạc nhiên khi broker sập.
Vấn đề cốt lõi là nhiều người xem hai công cụ này như hai thương hiệu của cùng một loại sản phẩm — kiểu như Pepsi và Coca. Sự thật là chúng được sinh ra để giải hai bài toán khác nhau về mặt triết lý. Nếu bạn hiểu được sự khác biệt triết lý đó, bạn sẽ không bao giờ phải tra Google "Kafka vs RabbitMQ" nữa — bạn sẽ tự biết chọn cái nào chỉ trong vài phút.
Bài này tập trung hoàn toàn vào việc so sánh và ra quyết định giữa hai lựa chọn cụ thể này. (Các managed options như SQS, Cloud Pub/Sub sẽ được bàn ở bài sau, còn Kafka được đào sâu về kiến trúc log ở các bài liên quan.)
Khái niệm cốt lõi
Hai mental model đối lập
Cách nhanh nhất để nhớ sự khác biệt là ghi nhớ hai câu:
Kafka là một distributed commit log. Hãy tưởng tượng một cuốn sổ cái khổng lồ, chỉ ghi thêm vào cuối (append-only), lưu trên đĩa, và không bao giờ xóa message ngay khi có người đọc. Message nằm đó theo thứ tự, mỗi message có một số thứ tự gọi là offset. Consumer tự chịu trách nhiệm nhớ mình đã đọc đến offset nào. Kafka giống một thư viện: sách được xếp lên kệ theo thứ tự, ai muốn đọc thì tự nhớ mình đọc tới trang nào. Bản thân broker khá "ngốc" (dumb broker) — nó không quyết định gửi gì cho ai, chỉ lưu trữ và cho đọc lại.
RabbitMQ là một smart broker, dumb consumer. Ở đây broker mới là bộ não. Nó nhận message, dựa vào exchange và routing rule để quyết định đẩy message vào queue nào, rồi chủ động push message tới consumer. Khi consumer xử lý xong và gửi lại tín hiệu ack (acknowledgement), message bị xóa khỏi queue. RabbitMQ giống một bưu tá thông minh: bạn đưa lá thư kèm địa chỉ, bưu tá tự tìm đúng hộp thư và bỏ vào; khi người nhận đã lấy thư, lá thư biến mất.
Từ hai mental model này, gần như mọi khác biệt khác được suy ra một cách logic.
Retention và khả năng đọc lại (replay)
Vì Kafka lưu message như một cuốn log trên đĩa, nó giữ message theo thời gian (ví dụ 7 ngày) hoặc theo dung lượng, bất kể đã có ai đọc hay chưa. Điều này cho phép một khả năng cực kỳ mạnh: replay. Bạn có thể thêm một consumer mới hôm nay và bảo nó đọc lại toàn bộ lịch sử từ offset 0. Bạn có thể phát hiện bug trong service xử lý dữ liệu, sửa xong rồi tua ngược offset để chạy lại 3 ngày dữ liệu. Đây là siêu năng lực của Kafka.
RabbitMQ thì mặc định message biến mất sau khi được ack. Nó là một hàng đợi (queue) theo đúng nghĩa "vào trước ra trước, xử lý xong thì hết". Không có khái niệm replay tự nhiên. Nếu bạn cần đọc lại, bạn phải tự lưu message vào đâu đó.
Mô hình phân phối: pull vs push
Kafka theo mô hình pull — consumer chủ động kéo message về theo nhịp của mình. Điều này giúp consumer chậm không bị "ngập" (overwhelm) và giúp Kafka đạt throughput cực cao bằng batch.
RabbitMQ theo mô hình push — broker đẩy message xuống consumer. Nhanh, độ trễ thấp, nhưng nếu consumer chậm thì phải dựa vào cơ chế prefetch để giới hạn.
Thứ tự và song song hóa
Kafka chia mỗi topic thành nhiều partition. Thứ tự chỉ được bảo đảm trong một partition, không phải toàn topic. Số consumer chạy song song tối đa trong một consumer group bằng số partition. Muốn các event của cùng một khách hàng đi đúng thứ tự? Bạn dùng customer_id làm key để chúng luôn rơi vào cùng một partition.
RabbitMQ bảo đảm thứ tự trong một queue (khi có một consumer), nhưng khi bạn scale ra nhiều consumer trên cùng queue để tăng thông lượng, thứ tự tổng thể không còn được bảo đảm chặt chẽ.
Routing linh hoạt
Đây là điểm RabbitMQ tỏa sáng. Với các loại exchange (direct, topic, fanout, headers), bạn có thể định tuyến message theo những quy tắc phức tạp: gửi message có routing key "order.vn.hcm" tới queue A, "order.vn.*" tới queue B, và tất cả tới queue log. Kafka gần như không có routing thông minh ở tầng broker — mọi logic "ai đọc topic nào" nằm ở phía consumer.
Độ phức tạp vận hành
Kafka nặng hơn đáng kể. Truyền thống cần cụm ZooKeeper (các phiên bản mới dùng KRaft để bỏ ZooKeeper), cần hiểu partition, replication factor, ISR, consumer lag. RabbitMQ nhẹ hơn, cài đặt và chạy nhanh, giao diện quản trị thân thiện. Với một đội nhỏ, chi phí vận hành là yếu tố quyết định thường bị đánh giá thấp.
Tình huống thực tế
Ví dụ 1 — Sàn TMĐT: RabbitMQ cho luồng xử lý đơn hàng
Hãy hình dung một sàn thương mại điện tử tầm trung ở Việt Nam, gọi là "ChợViệt", xử lý khoảng 40.000 đơn hàng mỗi ngày (đỉnh điểm dịp 11/11 lên khoảng 200.000). Khi một đơn được đặt, hệ thống cần: trừ tồn kho, tạo yêu cầu thanh toán, gửi email xác nhận, đẩy job cho kho đóng gói, và thông báo cho seller.
Đội kiến trúc chọn RabbitMQ. Vì sao?
- Mỗi đơn hàng là một task cần được xử lý đúng một lần rồi biến mất. Không ai cần "đọc lại đơn hàng của 3 ngày trước" ở tầng broker — dữ liệu đó đã nằm trong database.
- Họ cần routing thông minh: đơn thanh toán COD đi một đường, đơn ví điện tử đi đường khác, đơn có giá trị lớn hơn 5 triệu đi qua một queue kiểm tra gian lận riêng. RabbitMQ với topic exchange giải quyết việc này bằng vài dòng cấu hình.
- Họ cần retry và dead-letter queue tự nhiên: nếu gọi cổng thanh toán thất bại, message được đẩy sang một queue chờ rồi thử lại; sau 5 lần vẫn lỗi thì rơi vào dead-letter queue để người vận hành xem xét. RabbitMQ hỗ trợ pattern này rất mượt.
- Throughput vài trăm nghìn message/ngày nằm hoàn toàn trong khả năng của RabbitMQ mà không cần đội chuyên trách.
Ví dụ 2 — Nền tảng gọi xe: Kafka cho luồng dữ liệu real-time
Bây giờ hãy nghĩ về một nền tảng gọi xe kiểu Grab/Gojek hoạt động ở Đông Nam Á. Mỗi tài xế gửi vị trí GPS mỗi 3–4 giây. Với 500.000 tài xế đang online, đó là hơn 130.000 event vị trí mỗi giây, chạy 24/7. Cùng luồng dữ liệu này cần được tiêu thụ bởi nhiều hệ thống khác nhau: dịch vụ matching tài xế–khách, dịch vụ tính giá động (surge pricing), dịch vụ vẽ ETA, hệ thống phân tích dữ liệu, và mô hình machine learning dự đoán nhu cầu.
Đây là sân chơi của Kafka.
- Throughput khổng lồ: hàng trăm nghìn event/giây là bánh mì hằng ngày của Kafka nhờ append-only log và cơ chế batch.
- Nhiều consumer độc lập trên cùng một luồng: đội matching, đội pricing, đội analytics mỗi đội là một consumer group, đọc cùng một topic ở tốc độ của riêng mình, không ảnh hưởng nhau. Trong RabbitMQ, để một message tới nhiều nơi bạn phải nhân bản qua fanout và tự quản lý nhiều queue — cồng kềnh ở quy mô này.
- Replay: khi đội ML huấn luyện một mô hình dự đoán nhu cầu mới, họ tua lại 7 ngày dữ liệu vị trí từ Kafka. Không cần dựng thêm hạ tầng lưu trữ riêng.
- Thứ tự theo partition: dùng driver_id làm key để mọi event của một tài xế luôn đi đúng thứ tự trong một partition, trong khi vẫn scale song song trên hàng trăm partition.
Ví dụ 3 — Fintech chọn nhầm rồi phải chuyển đổi
Một startup fintech giả định tên "PayNhanh" ban đầu chỉ có một tính năng: gửi thông báo giao dịch qua app và SMS. Kiến trúc sư mới về, từng làm ở công ty lớn quen dùng Kafka, liền dựng luôn một cụm Kafka 3 node cho việc gửi khoảng 10.000 thông báo/ngày.
Sáu tháng sau vấn đề lộ ra: đội chỉ có 4 kỹ sư backend, không ai thực sự hiểu sâu Kafka. Họ tốn thời gian vật lộn với consumer lag, rebalancing khi deploy, cấu hình retention, và ba node ZooKeeper "ăn" RAM. Trong khi đó nhu cầu thực tế chỉ là một task queue đơn giản có retry. Cuối cùng họ chuyển sang RabbitMQ, giảm hạ tầng từ 6 node xuống 2 node, và giải phóng thời gian đội để làm tính năng.
Bài học: Đừng chọn công cụ theo "hào quang công nghệ" hay theo công ty cũ. Kafka mạnh nhưng có chi phí vận hành và nhận thức (cognitive load) thật. Với đội nhỏ và workload dạng task, RabbitMQ (hoặc thậm chí một managed queue) thường là quyết định trưởng thành hơn. Quy mô của đội cũng là một trục quyết định, không chỉ quy mô dữ liệu.
Hướng dẫn từng bước
Khi đứng trước lựa chọn, hãy đi qua các câu hỏi sau theo thứ tự. Câu trả lời của phần lớn sẽ nghiêng bạn về một phía rõ ràng.
- Bản chất của message là "sự kiện" hay "công việc"?
- Có bao nhiêu bên cần đọc cùng một message, và có cần đọc lại không?
- Throughput mục tiêu là bao nhiêu?
- Bạn cần routing phức tạp không?
- Đội của bạn lớn và trưởng thành đến đâu về vận hành?
- Yêu cầu về thứ tự?
- Nếu đa số câu trả lời mâu thuẫn nhau, hãy ưu tiên hai yếu tố: throughput cực cao và nhu cầu replay đẩy mạnh về Kafka; còn routing phức tạp và đội nhỏ đẩy mạnh về RabbitMQ. Và đừng ngại kết luận: "Cả hai" là một câu trả lời hợp lệ — nhiều công ty lớn dùng Kafka cho luồng dữ liệu và RabbitMQ cho task queue trong cùng một hệ thống.
Lỗi thường gặp & mẹo
- Chọn Kafka vì "sau này sẽ scale to". Đây là lỗi phổ biến nhất. Bạn trả giá vận hành ngay hôm nay cho một quy mô có thể không bao giờ tới. Hãy chọn theo nhu cầu 12–18 tháng tới, và nhớ rằng chuyển đổi broker là việc làm được, không phải án tử.
- Nghĩ RabbitMQ không thể scale. RabbitMQ xử lý được hàng chục nghìn message/giây một cách thoải mái, đủ cho phần lớn nghiệp vụ doanh nghiệp. Đừng loại nó chỉ vì nghe đồn "không đủ nhanh".
- Dùng Kafka như một task queue kiểu RabbitMQ. Cố nhét retry per-message, priority, hay ack từng message vào Kafka sẽ khiến bạn đi ngược thiết kế của nó và tự chuốc khổ. Kafka commit theo offset, không ack từng message như RabbitMQ.
- Quên rằng thứ tự của Kafka chỉ nằm trong partition. Nhiều người tưởng cả topic được sắp thứ tự. Nếu bạn cần thứ tự theo một thực thể, phải chọn key hợp lý.
- Bỏ qua consumer lag khi dùng Kafka. Consumer chậm hơn producer thì lag tăng dần và bạn có thể mất dữ liệu khi retention hết hạn. Phải giám sát lag như một chỉ số sống còn.
- Không thiết lập dead-letter khi dùng RabbitMQ. Message lỗi lặp vô hạn sẽ làm nghẽn queue. Luôn có chiến lược retry giới hạn và dead-letter queue.
- Mẹo: Nếu bạn đang ở trên cloud và đội nhỏ, hãy cân nhắc bản managed (Amazon MSK, Confluent Cloud cho Kafka; CloudAMQP cho RabbitMQ) để giảm gánh vận hành — nhưng đó là chủ đề của bài sau.
Bài tập thực hành
- Phân loại nhanh. Với mỗi kịch bản sau, hãy quyết định Kafka hay RabbitMQ và viết một câu lý do:
- Thiết kế cho ChợViệt. Giả sử sàn TMĐT ở ví dụ 1 muốn thêm tính năng "gợi ý sản phẩm bằng AI" dựa trên mọi hành vi xem/click của người dùng, với 5 đội khác nhau muốn dùng chung luồng dữ liệu hành vi này. Bạn có giữ nguyên RabbitMQ cho toàn bộ, hay đưa thêm Kafka vào? Vẽ sơ đồ ngắn giải thích message nào đi qua broker nào và vì sao.
- Bảng quyết định của riêng bạn. Dựa trên 7 câu hỏi ở phần Hướng dẫn, tự tạo một bảng một trang gồm các cột: Tiêu chí | Nghiêng Kafka | Nghiêng RabbitMQ. Đây sẽ là "cheat sheet" bạn dùng trong các buổi review kiến trúc thật.
- Phản biện. Tìm một lập luận thuyết phục để bảo vệ quyết định của startup fintech ở ví dụ 3 nếu họ giữ Kafka (ví dụ: họ chắc chắn sẽ chuyển sang mô hình event-driven trong 6 tháng tới). Bài tập này rèn cho bạn thấy quyết định kiến trúc luôn phụ thuộc bối cảnh, không có đáp án tuyệt đối.
Tóm tắt
Kafka và RabbitMQ không phải hai phiên bản của cùng một thứ — chúng là hai triết lý khác nhau. Kafka là một distributed commit log: broker "ngốc", lưu message trên đĩa theo offset, consumer tự kéo và tự nhớ vị trí, cho throughput cực cao và khả năng replay. RabbitMQ là một smart broker: broker thông minh định tuyến và push message tới consumer, message biến mất sau khi được ack, mạnh về routing linh hoạt và retry/dead-letter.
Quy tắc ghi nhớ: event stream cần nhiều người đọc, throughput lớn, cần đọc lại → Kafka; task queue cần định tuyến thông minh, xử lý-rồi-xóa, đội nhỏ → RabbitMQ. Và đừng quên hai trục quyết định thường bị bỏ sót: quy mô đội ngũ và chi phí vận hành, chứ không chỉ quy mô dữ liệu. Cuối cùng, "dùng cả hai cho hai mục đích khác nhau" là một câu trả lời hoàn toàn hợp lệ và rất phổ biến trong thực tế. Khi bạn hiểu được triết lý bên dưới, việc chọn công cụ chỉ còn là hệ quả logic của bài toán bạn đang giải.