Kafka Share Groups (Kafka Queues): nhiều worker cùng xử lý một partition Trung cấp
Thời gian đọc ước tính: 7 phút
Cần đọc trước
Vấn đề
Phần tiêu đề “Vấn đề”Topic image-jobs có một partition. Bạn tăng consumer trong group từ 1 lên 8 nhưng throughput không đổi: ở consumer group thường, một partition chỉ giao cho một consumer, bảy consumer còn lại ngồi chơi. Tăng partition đổi phân phối key và thứ tự, và không thể giảm lại.
Share Groups (còn gọi Kafka Queues, KIP-932) cho nhiều consumer lấy các record khác nhau từ cùng một partition. Mỗi record được giữ bằng lock có thời hạn rồi được ack từng cái. Producer vẫn ghi vào topic Kafka bình thường.
Trạng thái theo phiên bản
Phần tiêu đề “Trạng thái theo phiên bản”| Phiên bản | Trạng thái | Nguồn |
|---|---|---|
| 4.0 | Early access (chỉ cluster test, không nâng cấp lên 4.1 được) | Release announcement 4.0 |
| 4.1 | Preview, bật bằng kafka-features.sh (share.version=1) |
Upgrade notes |
| 4.2 | Production-ready | Upgrade notes và release announcement |
Lưu ý khi vận hành 4.2: cluster dưới 3 broker phải đặt share.coordinator.state.topic.replication.factor và share.coordinator.state.topic.min.isr về 1 trước khi dùng; docs ghi 4.2.1 sửa một deadlock nghiêm trọng trong đường share group, nên dùng bản vá mới nhất.
Repo này dùng broker cp-kafka:7.6.0 (Kafka 3.6, ZooKeeper) và KafkaJS 2.2 cho các demo cũ, nên không chạy được Share Groups; KafkaJS không có Share Consumer API. Ví dụ bên dưới là mô hình RAM.
So sánh
Phần tiêu đề “So sánh”| Consumer group | Share group | RabbitMQ quorum queue | |
|---|---|---|---|
| Đơn vị chia việc | Partition | Record | Message |
| Số worker hữu ích | Tối đa bằng số partition | Không bị giới hạn bởi partition | Nhiều consumer trên một queue |
| Thứ tự xử lý | Theo offset trong partition | Không bảo đảm | Không bảo đảm khi có nhiều consumer |
| Ack | Commit offset liên tục | Từng record (ACCEPT/RELEASE/REJECT/RENEW) |
Từng message (ack/nack) |
| Giới hạn giao lại | Không có | group.share.delivery.count.limit, mặc định 5 |
delivery-limit, mặc định 20 từ 4.0 |
| Lock hết hạn | Không có | share.record.lock.duration.ms, mặc định 30 giây |
Theo consumer ack timeout |
| Replay | Seek offset | Không phải mục tiêu | Không (queue thường) |
| Nhiều group đọc cùng dữ liệu | Có | Có (retention của topic vẫn áp dụng) | Cần fan-out qua exchange |
Cheat-sheet config (Kafka 4.2)
Phần tiêu đề “Cheat-sheet config (Kafka 4.2)”| Config | Mặc định | Ghi chú |
|---|---|---|
group.share.record.lock.duration.ms (broker) / share.record.lock.duration.ms (group) |
30000 | Tối thiểu 1000 ở group config |
group.share.delivery.count.limit (broker) |
5 | Quá số lần giao thì record không được giao nữa |
group.share.partition.max.record.locks (broker) |
2000 | Trần số record đang bị lock mỗi partition |
share.auto.offset.reset (group) |
latest |
Share partition mới bắt đầu từ cuối log, không phải từ đầu |
share.isolation.level (group) |
read_uncommitted |
read_committed để bỏ qua transaction chưa commit |
share.session.timeout.ms |
45000 | Phát hiện client chết |
share.heartbeat.interval.ms |
5000 |
Chú ý share.auto.offset.reset=latest: record produce trước khi share group đầu tiên subscribe sẽ không được giao; muốn xử lý backlog cũ phải đặt earliest.
Cơ chế
Phần tiêu đề “Cơ chế”offset 7A acquire offset 7. Record này bị khóa tạm thời cho A, chưa có ack thành công.
Kafka chọn record còn khả dụng và acquire cho một consumer bằng lock có thời hạn. Trong lúc lock còn hiệu lực, record không giao cho consumer khác; record kế tiếp của cùng partition có thể được worker khác lấy.
Với explicit acknowledgement (Java client):
| Kết quả | AcknowledgeType |
Ý nghĩa |
|---|---|---|
| Thành công | ACCEPT |
Đánh dấu đã xử lý |
| Lỗi tạm | RELEASE |
Thả để giao lại |
| Không thể xử lý | REJECT |
Không giao lại trong group |
| Đang làm lâu | RENEW |
Gia hạn lock |
acknowledge() chỉ đánh dấu ý định ở client; ack được gửi qua lần poll tiếp theo hoặc commitSync()/commitAsync(). Chế độ implicit mặc định coi record của lần poll trước là thành công khi poll tiếp, tiện nhưng nguy hiểm nếu batch có record lỗi. Dùng explicit mode cho job cần phân loại lỗi.
Ba tình huống cần nghĩ trước
Phần tiêu đề “Ba tình huống cần nghĩ trước”- Hoàn tất đảo thứ tự. A lấy offset 7, B lấy offset 8 và xong trước. Nếu 8 phụ thuộc 7, kết quả sai. Share group không hứa thứ tự hoàn tất.
- Lock hết hạn khi worker còn chạy. A chậm quá 30 giây, record 7 được giao cho B, A vẫn gọi API ngoài: side effect trùng. Lock ở broker không thay idempotency; việc dài dùng
RENEW. - Lỗi vĩnh viễn.
REJECThoặc hết số lần giao chỉ dừng việc giao lại trong share group, không tự tạo dead-letter topic. Bạn phải tự thiết kế nơi lưu lỗi, alert và replay.
Chọn share group hay RabbitMQ quorum queue
Phần tiêu đề “Chọn share group hay RabbitMQ quorum queue”| Tình huống | Chọn |
|---|---|
| Đã có Kafka, job độc lập, cần nhiều worker hơn số partition, cùng dữ liệu còn được analytics đọc | Share group (Kafka 4.2+) |
| Cần routing theo pattern, priority, TTL/DLX phong phú | RabbitMQ (queue types) |
| Thứ tự theo key quan trọng | Consumer group, không phải share group |
| Chưa vận hành Kafka 4.2 | Giữ consumer group với đủ partition, hoặc RabbitMQ |
Khi nào dùng / không dùng
Phần tiêu đề “Khi nào dùng / không dùng”- Dùng: job độc lập (thumbnail, gửi email, gọi API), workload “queue trên Kafka”.
- Không dùng: state machine theo entity, ledger cần thứ tự, bất kỳ nơi nào offset order là invariant.
- Không dùng nếu bạn chưa nâng được cluster lên 4.2 và client lên Share Consumer API.
Ví dụ thực tế
Phần tiêu đề “Ví dụ thực tế”Mô hình RAM (không phải output từ Kafka thật): A acquire 7, B acquire 8 và accept, lock 7 hết hạn rồi B nhận lại 7 ở lần giao thứ hai; dòng cuối completion order: 8, 7. Lock trong demo là 30 ms cho chạy nhanh; mặc định thật là 30 giây.
npx --prefix examples ts-node examples/src/kafka-share-groups/share-group-model.tsĐể kiểm chứng thật: cluster Kafka 4.2+ và Java KafkaShareConsumer, topic một partition, hai consumer cùng group.id với share.acknowledgement.mode=explicit, dừng một worker sau acquire và trước ack để quan sát giao lại.
Trade-offs
Phần tiêu đề “Trade-offs”Khi share group hợp
- Nhiều worker trên một partition, không bị giới hạn bởi partition count.
- Ack và retry ở mức record, phân loại lỗi từng job.
- Vẫn là topic Kafka: group khác đọc độc lập.
Đánh đổi
- Mất bảo đảm thứ tự hoàn tất.
- Lock hết hạn, ack chưa commit và retry vẫn gây xử lý trùng.
- Cần broker và client 4.2+; thêm việc vận hành delivery attempts, lỗi, retention.
Checklist production
Phần tiêu đề “Checklist production”- Chỉ dùng cho job độc lập; xác nhận không có invariant về thứ tự.
- Mọi job có
jobIdổn định và ghi kết quả idempotent. - Dùng explicit acknowledgement;
ACCEPTchỉ sau khi kết quả bền. - Đặt lock duration lớn hơn thời gian xử lý p99, dùng
RENEWcho việc dài. - Tự thiết kế nơi lưu lỗi cho
REJECTvà job hết lượt giao (mặc định 5). - Đặt
share.auto.offset.resetcó chủ đích (earliestnếu cần xử lý backlog). - Theo dõi tuổi record so với retention của topic.
Sai lầm thường gặp
Phần tiêu đề “Sai lầm thường gặp”Tóm tắt
Phần tiêu đề “Tóm tắt”Tự kiểm tra: (1) Một partition và tám worker: consumer group dùng được mấy, share group thì sao? (2) Offset 8 có thể hoàn tất trước 7 không? (3) Vì sao worker A còn chạy sau khi lock hết hạn có thể gây trùng side effect?