Mở đầu — vì sao bài này quan trọng
Ở Bài 30 và Bài 31, chúng ta đã hiểu về message broker nói chung và so sánh cặp đôi kinh điển Kafka với RabbitMQ. Nhưng có một sự thật mà rất nhiều đội kỹ thuật ở Việt Nam bỏ qua khi thiết kế kiến trúc: câu hỏi thực sự không phải là "Kafka hay RabbitMQ", mà là "chúng ta có thực sự muốn tự vận hành một broker không?".
Tự cài đặt và vận hành một cụm Kafka gồm broker, ZooKeeper (hoặc KRaft), monitoring, backup, tuning partition, xử lý rebalance khi node chết... là một công việc toàn thời gian cho ít nhất một hoặc hai kỹ sư. Với một startup 15 người ở TP.HCM đang chạy đua ra mắt sản phẩm, việc "nuôi" một cụm Kafka self-managed có thể ngốn nhiều thời gian hơn cả việc xây tính năng chính.
Đó là lý do các managed messaging service ra đời: AWS SQS, Amazon MSK (Managed Kafka), Confluent Cloud, và Google Cloud Pub/Sub. Chúng đánh đổi một phần khả năng kiểm soát và một khoản chi phí trên mỗi message để lấy về thứ quý giá nhất với đội nhỏ: thời gian và sự yên tâm vận hành.
Bài này giúp bạn ra một quyết định kiến trúc cụ thể: khi đã chọn dùng dịch vụ managed, nên chọn SQS, Kafka managed, hay Pub/Sub — và quan trọng hơn là biết vì sao. Đây là loại quyết định mà nếu chọn sai, sáu tháng sau bạn sẽ phải viết lại nguyên tầng messaging, một việc cực kỳ tốn kém.
Khái niệm cốt lõi
Trước khi so sánh, phải phân biệt một khác biệt nền tảng mà rất nhiều người nhầm lẫn: queue khác pub/sub log.
Queue vs Pub/Sub — hai mô hình khác nhau
Một queue (hàng đợi) hoạt động theo nguyên tắc: mỗi message được đưa vào, và khi một consumer lấy ra xử lý xong thì message đó biến mất. Message được tiêu thụ đúng một lần bởi một worker. Đây là mô hình "chia việc" — nhiều worker cùng rút việc từ một hàng đợi để làm song song.
Một pub/sub hoặc log-based system hoạt động khác hẳn: một message được publish ra một topic, và nhiều nhóm consumer khác nhau đều có thể đọc cùng message đó một cách độc lập. Message không biến mất sau khi được đọc — nó nằm lại trong log theo thời gian retention. Đây là mô hình "phát thanh" — một sự kiện xảy ra, nhiều bộ phận cùng nghe và phản ứng theo cách riêng.
Ghi nhớ điều này vì nó quyết định lựa chọn: SQS về bản chất là queue, còn Kafka và Pub/Sub về bản chất là hệ thống pub/sub.
AWS SQS — hàng đợi đơn giản, trả tiền theo message
SQS (Simple Queue Service) là dịch vụ lâu đời nhất của AWS, và cũng là đơn giản nhất. Nó là một queue thuần túy. Có hai loại:
- Standard Queue: throughput gần như không giới hạn, nhưng không đảm bảo thứ tự (unordered) và giao hàng theo kiểu at-least-once — nghĩa là một message có thể bị giao trùng hơn một lần. Ứng dụng của bạn phải tự xử lý idempotency (đã học ở Bài 39).
- FIFO Queue: đảm bảo thứ tự (First-In-First-Out) trong cùng một message group, và giao hàng exactly-once ở mức best-effort. Đổi lại, throughput bị giới hạn (mặc định khoảng 300 message/giây mỗi API, hoặc 3.000/giây khi bật batching, có thể xin nâng lên tới 70.000/giây với high throughput mode).
Điểm cần lưu ý: SQS không giữ lại message sau khi đã được xử lý, và message có thời gian tồn tại tối đa 14 ngày. Nó không phải nơi để "replay lịch sử sự kiện". SQS cũng thường đi kèm SNS (Simple Notification Service) khi bạn cần fan-out một message tới nhiều queue — cặp SNS + SQS là cách AWS mô phỏng pub/sub.
Managed Kafka — MSK và Confluent Cloud
Nếu bạn cần đúng ngữ nghĩa của Kafka: log bền vững, replay được, giữ message theo retention dài (nhiều ngày, thậm chí vô hạn), throughput cực cao với ordering theo partition — thì bạn cần Kafka. Có hai cách dùng managed:
- Amazon MSK: AWS lo phần hạ tầng (broker, ZooKeeper/KRaft, vá lỗi, HA), nhưng bạn vẫn phải suy nghĩ về số broker, kích thước instance, số partition. Có bản MSK Serverless để bớt phải tune capacity.
- Confluent Cloud: dịch vụ do chính công ty tạo ra Kafka vận hành, chạy được trên AWS/GCP/Azure, đi kèm hệ sinh thái Schema Registry, Kafka Connect, ksqlDB. Trả phí theo throughput và storage.
Google Cloud Pub/Sub — pub/sub toàn cầu, tự co giãn
Cloud Pub/Sub là dịch vụ pub/sub managed của Google, được thiết kế để tự động co giãn (auto-scaling) mà không cần bạn khai báo partition hay capacity. Bạn tạo một topic, gắn các subscription vào đó. Mỗi subscription là một luồng đọc độc lập.
Đặc điểm nổi bật:
- At-least-once mặc định, có tùy chọn exactly-once delivery trong một subscription.
- Không cần khai báo partition — Google tự lo scaling. Đây là khác biệt lớn so với Kafka, nơi bạn phải tự tính số partition.
- Ordering là tùy chọn (phải bật ordering key), không phải mặc định.
- Retention mặc định 7 ngày, có thể kéo dài hơn.
- Hỗ trợ cả push (Pub/Sub chủ động gọi endpoint của bạn) lẫn pull (consumer tự kéo).
Bảng so sánh nhanh để định vị
| Tiêu chí | SQS | Managed Kafka (MSK/Confluent) | Cloud Pub/Sub |
|---|---|---|---|
| Mô hình | Queue | Log / Pub-Sub | Pub/Sub |
| Ordering | Chỉ FIFO queue | Theo partition | Tùy chọn (ordering key) |
| Replay lịch sử | Không | Có (retention dài) | Giới hạn (tối đa retention) |
| Nhiều consumer group | Không (cần SNS fan-out) | Có | Có (nhiều subscription) |
| Quản lý partition | Không | Có, phải tune | Không |
| Chi phí | Theo request | Theo giờ cụm + storage | Theo lượng data |
| Vận hành | Cực nhẹ | Nặng nhất | Nhẹ |
Tình huống thực tế
Ví dụ 1 — Sàn TMĐT xử lý đơn hàng bằng SQS
Một sàn thương mại điện tử tầm trung ở Hà Nội (giả định tên "ChợViệt") có khoảng 40.000 đơn hàng mỗi ngày. Kiến trúc gồm: service đặt hàng nhận request, rồi cần kích hoạt một loạt việc nền — trừ kho, gửi email xác nhận, tạo yêu cầu giao hàng cho đối tác vận chuyển, cộng điểm thưởng.
Đội kỹ thuật ban đầu định dựng Kafka vì "nghe nói Kafka mạnh". Nhưng khi phân tích kỹ, họ nhận ra nhu cầu thực chỉ là: mỗi đơn cần được xử lý đúng một lần bởi một worker, thứ tự tuyệt đối giữa các đơn khác nhau không quan trọng, và họ không cần replay lịch sử. Đây đúng là bài toán queue.
Họ chọn SQS Standard cho các tác vụ như gửi email và cộng điểm (những việc idempotent, trùng lặp không sao nếu xử lý đúng), và SQS FIFO với message group theo order_id cho luồng trừ kho (để tránh race condition trên cùng một sản phẩm). Với ~200.000 request mỗi ngày, hóa đơn SQS chưa tới 20 USD/tháng. Quan trọng hơn: không kỹ sư nào phải thức đêm vá cụm broker.
Bài học rút ra: Đừng chọn Kafka chỉ vì nó "xịn". Khi nhu cầu là chia việc nền, không cần replay, không cần ordering toàn cục — SQS đơn giản hơn, rẻ hơn, và gần như không tốn công vận hành. Bắt đầu bằng công cụ đơn giản nhất giải quyết đúng bài toán.
Ví dụ 2 — Fintech cần replay và audit, chọn Kafka managed
Một công ty fintech tại Singapore phục vụ thị trường Đông Nam Á (giả định "PayFlow") xử lý giao dịch ví điện tử. Yêu cầu nghiệp vụ khắt khe: mọi thay đổi số dư phải là một sự kiện bất biến, phải audit được, và khi phát hiện lỗi tính toán ở tuần trước, họ phải replay lại toàn bộ event để tái dựng trạng thái đúng. Đồng thời, nhiều bộ phận cùng cần đọc luồng giao dịch: đội chống gian lận, đội báo cáo tài chính, đội gửi thông báo.
Đây là bài toán mà SQS hoàn toàn không giải được — SQS xóa message sau khi xử lý, không replay được, và một message chỉ tới được một consumer. PayFlow chọn Confluent Cloud (Kafka managed). Lý do chọn Confluent thay vì tự dựng Kafka: đội chỉ có 3 kỹ sư backend, không đủ người để vận hành cụm Kafka đảm bảo 99,99% uptime cho một hệ thống tiền bạc. Confluent lo phần vận hành, họ tập trung vào logic.
Họ dùng retention dài trên topic transaction-events, mỗi consumer group (fraud, reporting, notification) đọc độc lập cùng dữ liệu. Khi cần điều tra sự cố, họ reset offset về mốc thời gian và replay. Chi phí cao hơn SQS nhiều lần (hàng nghìn USD/tháng), nhưng với đặc thù fintech, đây là chi phí bắt buộc để có tính đúng đắn và audit.
Bài học rút ra: Khi bạn cần replay lịch sử, retention dài, và nhiều consumer group độc lập — đặc biệt trong các mô hình event sourcing (Bài 34) — Kafka là lựa chọn đúng. Và nếu đội bạn nhỏ, hãy dùng bản managed để không phải gánh vận hành.
Ví dụ 3 — Startup analytics đa vùng chọn Cloud Pub/Sub
Một startup phân tích hành vi người dùng ứng dụng di động, khách hàng trải khắp Đông Nam Á, hạ tầng đặt trên Google Cloud (giả định "InsightLab"). Lượng event từ SDK gửi về rất co giãn thất thường: ngày thường 5.000 event/giây, nhưng khi một khách hàng lớn chạy chiến dịch, vọt lên 80.000 event/giây trong vài giờ rồi lại xẹp xuống.
Vấn đề với Kafka trong tình huống này: bạn phải tính trước số partition để chịu được đỉnh 80.000 event/giây, nhưng phần lớn thời gian lại lãng phí capacity đó. Việc re-partition khi tải tăng cũng phiền phức. Vì đã ở sẵn trên GCP và cần auto-scaling không cần tune, InsightLab chọn Cloud Pub/Sub.
Họ publish event vào topic mobile-events, gắn nhiều subscription: một cho pipeline nạp vào BigQuery (Bài 53), một cho real-time dashboard, một cho hệ thống cảnh báo bất thường. Pub/Sub tự co giãn theo đỉnh mà đội không phải làm gì. Khi cần một luồng xử lý sự kiện theo thứ tự cho từng người dùng, họ bật ordering key theo user_id.
Bài học rút ra: Khi tải rất biến động, bạn không muốn quản lý partition, và bạn đã ở trong hệ sinh thái GCP — Pub/Sub cho bạn mô hình pub/sub mạnh mẽ với chi phí vận hành gần như của SQS. Sự "vô hình" của việc scaling chính là giá trị lớn nhất.
Hướng dẫn từng bước
Đây là quy trình ra quyết định bạn có thể áp dụng trực tiếp khi thiết kế:
Bước 1 — Xác định mô hình: queue hay pub/sub? Hỏi: một message có cần đến nhiều nhóm consumer độc lập không? Nếu chỉ cần một worker xử lý rồi message biến mất → queue → nghiêng về SQS. Nếu nhiều bộ phận cùng cần nghe một sự kiện → pub/sub → nghiêng về Kafka hoặc Pub/Sub.
Bước 2 — Xác định nhu cầu replay và retention. Hỏi: bạn có cần đọc lại các message cũ (replay) để tái dựng trạng thái, debug, hay audit không? Nếu có → loại SQS ngay, chọn Kafka (retention dài nhất) hoặc Pub/Sub (retention giới hạn hơn). Nếu message dùng một lần rồi bỏ → SQS đủ.
Bước 3 — Xác định yêu cầu ordering.
Cần thứ tự tuyệt đối theo một khóa (ví dụ theo user_id, order_id)? SQS FIFO, Kafka (theo partition), hoặc Pub/Sub (ordering key) đều làm được. Nếu không cần ordering, mọi lựa chọn đều thoải mái hơn về throughput.
Bước 4 — Nhìn vào hệ sinh thái cloud hiện tại. Bạn đang ở AWS hay GCP? Đừng kéo một dịch vụ chéo cloud chỉ vì nó "hay hơn" — chi phí egress và độ trễ liên vùng sẽ cắn bạn. Ở AWS: SQS hoặc MSK. Ở GCP: Pub/Sub. Confluent Cloud là lựa chọn trung lập nếu bạn muốn Kafka thật mà không lệ thuộc một cloud.
Bước 5 — Ước tính chi phí theo mô hình thật. SQS tính theo số request — rất rẻ ở tải thấp, nhưng ở tải cực cao hàng tỷ request/tháng thì cần tính lại. Kafka managed tính theo giờ cụm + storage — có chi phí sàn cố định dù tải thấp. Pub/Sub tính theo lượng data. Hãy đưa con số tải thực tế vào máy tính chi phí của từng nhà cung cấp.
Bước 6 — Đánh giá năng lực vận hành của đội. Đội bạn có kỹ sư đủ giỏi và đủ thời gian để tune Kafka không? Nếu không, đừng chọn thứ đòi hỏi vận hành nặng. Một hệ thống "kém xịn" nhưng đội vận hành nổi luôn tốt hơn một hệ thống "xịn" nhưng thường xuyên hỏng vì không ai chăm được.
Bước 7 — Ghi lại quyết định thành ADR. Như đã học ở Bài 4 và Bài 29, hãy viết lại lựa chọn này thành một ADR: bối cảnh, các phương án cân nhắc, tiêu chí, quyết định cuối và hệ quả. Sáu tháng sau khi có người hỏi "sao hồi đó không dùng Kafka?", bạn có câu trả lời bằng văn bản.
Lỗi thường gặp & mẹo
Lỗi 1 — Dùng SQS rồi cố mô phỏng pub/sub bằng cách tự nhân bản message. Khi bạn thấy mình viết code để copy một message vào nhiều queue thủ công, đó là dấu hiệu bạn đang chọn sai công cụ. Hãy dùng SNS + SQS (fan-out chuẩn của AWS) hoặc chuyển hẳn sang Pub/Sub/Kafka.
Lỗi 2 — Chọn Kafka cho một hệ thống chỉ có vài trăm message mỗi ngày. Kafka có chi phí sàn cố định và độ phức tạp vận hành cao. Dùng Kafka cho tải bé giống như thuê xe container để chở một thùng hàng. Ở tải thấp, SQS hoặc Pub/Sub gần như luôn hợp lý hơn.
Lỗi 3 — Quên rằng SQS Standard là at-least-once. Rất nhiều lỗi production đến từ việc coi mỗi message chỉ tới đúng một lần. Với SQS Standard, message có thể trùng. Consumer của bạn bắt buộc phải idempotent — thiết kế để xử lý cùng một message hai lần vẫn cho kết quả đúng.
Lỗi 4 — Không cấu hình Dead Letter Queue (DLQ). Khi một message xử lý thất bại liên tục, nếu không có DLQ nó sẽ quay vòng mãi và làm nghẽn hệ thống. Luôn cấu hình DLQ cho SQS và cơ chế tương đương (dead-letter topic) cho Pub/Sub.
Lỗi 5 — Tính sai số partition Kafka ngay từ đầu. Tăng partition thì dễ, nhưng giảm thì không. Đặt quá nhiều partition gây lãng phí và tăng độ trễ; quá ít thì giới hạn throughput. Nếu bạn ghét việc phải đau đầu về partition, đó là một lý do để nghiêng về Pub/Sub.
Mẹo — Bắt đầu managed, đừng self-host quá sớm. Với hầu hết đội ở Việt Nam, chi phí một kỹ sư dành để vận hành broker tự dựng còn đắt hơn khoản chênh lệch giá của bản managed. Chỉ cân nhắc self-host khi quy mô đủ lớn để khoản tiết kiệm vượt hẳn chi phí nhân sự vận hành.
Mẹo — Đừng để bị khóa cứng vào vendor. Đặt tầng messaging sau một interface trừu tượng (ports & adapters, Bài 14). Nếu sau này cần đổi từ SQS sang Pub/Sub, bạn chỉ thay adapter chứ không phải viết lại logic nghiệp vụ.
Bài tập thực hành
Bài tập 1 — Phân loại nhu cầu. Với mỗi tình huống sau, hãy quyết định nên dùng SQS, Kafka managed, hay Pub/Sub, và giải thích trong 2-3 câu:
- Một hệ thống gửi SMS OTP, mỗi OTP xử lý một lần rồi bỏ, tải ~1.000/phút, hạ tầng trên AWS.
- Một nền tảng ride-hailing cần luồng vị trí tài xế được nhiều service đọc (định giá, bản đồ, chống gian lận) và cần replay khi điều tra sự cố.
- Một pipeline thu thập log ứng dụng, tải dao động mạnh, hạ tầng trên GCP, không muốn quản lý partition.
Bài tập 3 — Viết ADR. Chọn một trong ba tình huống ở Bài tập 1, viết một ADR hoàn chỉnh theo cấu trúc đã học ở Bài 4: Bối cảnh, Các phương án, Tiêu chí đánh giá, Quyết định, Hệ quả (tích cực và tiêu cực).
Bài tập 4 — Thiết kế chống trùng. Giả sử bạn dùng SQS Standard cho việc cộng điểm thưởng khách hàng. Vì là at-least-once, một message cộng điểm có thể tới hai lần. Hãy mô tả cơ chế idempotency để đảm bảo một giao dịch chỉ được cộng điểm đúng một lần (gợi ý: dùng một khóa idempotency lưu vào database).
Tóm tắt
- Câu hỏi cốt lõi trước tiên là queue hay pub/sub: SQS là queue (một message, một consumer, xong là biến mất), còn Kafka và Pub/Sub là pub/sub (một message, nhiều consumer group độc lập, giữ lại theo retention).
- SQS là lựa chọn đơn giản, rẻ, gần như không cần vận hành, trả tiền theo request. Standard là unordered + at-least-once; FIFO là ordered + exactly-once best-effort nhưng giới hạn throughput. Phù hợp khi chia việc nền, không cần replay.
- Kafka managed (MSK, Confluent Cloud) khi bạn cần retention dài, replay lịch sử, nhiều consumer group, ordering theo partition — điển hình cho event sourcing và audit. Đổi lại chi phí và độ phức tạp cao nhất.
- Cloud Pub/Sub cho mô hình pub/sub tự co giãn không cần quản lý partition, lý tưởng khi tải biến động mạnh và bạn đã ở trên GCP.
- Ba yếu tố quyết định thực tế: mô hình dữ liệu (queue/pub-sub, replay), hệ sinh thái cloud hiện tại, và năng lực vận hành của đội. Ưu tiên bản managed cho đội nhỏ, và luôn ghi lại lựa chọn thành ADR.