Apache Kafka 4.x: Kỷ nguyên Event Streaming không còn ZooKeeper

Posted on: 4/26/2026 7:15:51 AM

Apache Kafka vừa đánh dấu cột mốc lịch sử lớn nhất kể từ ngày ra đời: loại bỏ hoàn toàn ZooKeeper. Với phiên bản 4.0 (tháng 3/2025) và 4.2 (tháng 2/2026), Kafka không chỉ đơn giản hóa kiến trúc mà còn mang đến những tính năng mới thay đổi hoàn toàn cách chúng ta xây dựng hệ thống event-driven. Bài viết này sẽ đi sâu vào KRaft mode, Share Groups (Queues), Consumer Rebalance Protocol thế hệ mới, và cách tích hợp Kafka 4.x với .NET.

80%+ Fortune 100 sử dụng Kafka
7 triệu+ Instances trên toàn cầu
4.2 Phiên bản mới nhất (02/2026)
0 Dòng code ZooKeeper còn lại

1. KRaft Mode: Vĩnh biệt ZooKeeper

Suốt hơn một thập kỷ, ZooKeeper là thành phần không thể thiếu trong mọi deployment Kafka. Nó đảm nhận việc quản lý metadata, bầu chọn leader cho partition, và lưu trữ cấu hình cluster. Nhưng ZooKeeper cũng là nguồn gốc của vô số vấn đề vận hành: thêm một hệ thống phân tán phải quản lý, giới hạn số lượng partition (~200K), và bottleneck khi metadata thay đổi.

KRaft (Kafka Raft) là giải pháp: Kafka tự quản lý metadata của chính mình thông qua giao thức Raft consensus, được tích hợp trực tiếp vào broker. Không cần cluster ZooKeeper riêng biệt. Không cần vận hành thêm hệ thống nào.

graph LR
    subgraph "Kafka < 4.0 (ZooKeeper)"
        P1[Producer] --> B1[Broker 1]
        P1 --> B2[Broker 2]
        P1 --> B3[Broker 3]
        B1 --> ZK[ZooKeeper Ensemble]
        B2 --> ZK
        B3 --> ZK
        ZK --> ZK1[ZK Node 1]
        ZK --> ZK2[ZK Node 2]
        ZK --> ZK3[ZK Node 3]
        C1[Consumer] --> B1
        C1 --> B2
        C1 --> B3
    end

    style ZK fill:#ff9800,stroke:#e65100,color:#fff
    style ZK1 fill:#ffe0b2,stroke:#ff9800,color:#333
    style ZK2 fill:#ffe0b2,stroke:#ff9800,color:#333
    style ZK3 fill:#ffe0b2,stroke:#ff9800,color:#333
    style B1 fill:#e94560,stroke:#fff,color:#fff
    style B2 fill:#e94560,stroke:#fff,color:#fff
    style B3 fill:#e94560,stroke:#fff,color:#fff
    style P1 fill:#2c3e50,stroke:#fff,color:#fff
    style C1 fill:#2c3e50,stroke:#fff,color:#fff

Kiến trúc Kafka trước 4.0: cần cluster ZooKeeper riêng biệt

graph LR
    subgraph "Kafka 4.x (KRaft)"
        P2[Producer] --> KB1[Broker 1]
        P2 --> KB2[Broker 2]
        P2 --> KB3[Broker 3]
        KB1 ---|Raft Consensus| KB2
        KB2 ---|Raft Consensus| KB3
        KB3 ---|Raft Consensus| KB1
        C2[Consumer] --> KB1
        C2 --> KB2
        C2 --> KB3
    end

    style KB1 fill:#4CAF50,stroke:#fff,color:#fff
    style KB2 fill:#4CAF50,stroke:#fff,color:#fff
    style KB3 fill:#4CAF50,stroke:#fff,color:#fff
    style P2 fill:#2c3e50,stroke:#fff,color:#fff
    style C2 fill:#2c3e50,stroke:#fff,color:#fff

Kiến trúc Kafka 4.x: KRaft tích hợp trực tiếp, không cần ZooKeeper

Những cải tiến cốt lõi của KRaft

Tiêu chíZooKeeper ModeKRaft Mode
Số partition tối đa/cluster~200,000Hàng triệu
Thời gian khởi động clusterPhút (phụ thuộc ZK)Giây
Thành phần cần vận hànhKafka + ZooKeeperChỉ Kafka
Controller failoverChậm (ZK session timeout)Nhanh (Raft leader election)
Metadata propagationAsync qua ZK watchersEvent-driven qua metadata log
Security model2 hệ thống ACL riêngThống nhất 1 hệ thống

KRaft Controller vs Broker

Trong KRaft mode, bạn có thể chạy node ở chế độ controller-only, broker-only, hoặc combined. Với production cluster lớn, khuyến nghị tách riêng controller nodes (thường 3 hoặc 5 nodes) khỏi broker nodes để đảm bảo metadata management không bị ảnh hưởng bởi traffic xử lý message.

2. Share Groups: Khi Kafka cũng có Queue

Trước Kafka 4.x, mỗi partition chỉ có thể được consume bởi đúng một consumer trong cùng consumer group. Điều này đảm bảo ordering nhưng lại tạo ra bottleneck: nếu bạn có 10 partitions nhưng cần 20 workers xử lý, bạn phải re-partition topic — một thao tác tốn kém và gây gián đoạn.

Share Groups (KIP-932) giải quyết triệt để vấn đề này bằng cách cho phép nhiều consumer cùng đọc từ một partition với cơ chế acknowledge từng record riêng lẻ — giống hệt như một message queue truyền thống (RabbitMQ, SQS) nhưng chạy trên hạ tầng Kafka.

graph TD
    T[Topic: order-processing] --> P1[Partition 0]
    T --> P2[Partition 1]
    T --> P3[Partition 2]

    subgraph "Consumer Group truyền thống"
        P1 --> CG1[Consumer A]
        P2 --> CG2[Consumer B]
        P3 --> CG3[Consumer C]
    end

    subgraph "Share Group (Kafka 4.x)"
        P1 -.-> SG1[Worker 1]
        P1 -.-> SG2[Worker 2]
        P2 -.-> SG1
        P2 -.-> SG3[Worker 3]
        P3 -.-> SG2
        P3 -.-> SG3
    end

    style T fill:#e94560,stroke:#fff,color:#fff
    style P1 fill:#f8f9fa,stroke:#e94560,color:#333
    style P2 fill:#f8f9fa,stroke:#e94560,color:#333
    style P3 fill:#f8f9fa,stroke:#e94560,color:#333
    style CG1 fill:#2c3e50,stroke:#fff,color:#fff
    style CG2 fill:#2c3e50,stroke:#fff,color:#fff
    style CG3 fill:#2c3e50,stroke:#fff,color:#fff
    style SG1 fill:#4CAF50,stroke:#fff,color:#fff
    style SG2 fill:#4CAF50,stroke:#fff,color:#fff
    style SG3 fill:#4CAF50,stroke:#fff,color:#fff

So sánh Consumer Group truyền thống vs Share Group: workers có thể chia sẻ partition

Các loại Acknowledgement trong Share Groups

Kafka 4.2 bổ sung thêm kiểu RENEW bên cạnh ACCEPT và REJECT, cho phép consumer gia hạn thời gian xử lý khi gặp tác vụ chạy lâu:

  • ACCEPT: Record đã được xử lý thành công, đánh dấu hoàn thành
  • REJECT: Record xử lý thất bại, trả lại cho Share Group để worker khác nhận
  • RENEW (mới trong 4.2): Gia hạn thời gian xử lý cho các tác vụ chạy lâu như ML inference hay video transcoding

Share Groups không thay thế Consumer Groups

Share Groups được thiết kế cho các use case point-to-point messaging nơi ordering không quan trọng bằng throughput. Nếu ứng dụng yêu cầu xử lý theo đúng thứ tự trong partition (ví dụ: event sourcing, CDC), hãy tiếp tục sử dụng Consumer Groups truyền thống.

3. Consumer Rebalance Protocol thế hệ mới (KIP-848)

Một trong những "nỗi đau" lớn nhất khi vận hành Kafka là stop-the-world rebalance. Mỗi khi một consumer join hoặc leave group, toàn bộ consumer trong group phải dừng xử lý, revoke partitions, và chờ reassignment. Với consumer group lớn (hàng trăm instances), quá trình này có thể mất hàng phút — nghĩa là hàng phút không xử lý được message nào.

KIP-848 mang đến giao thức rebalance mới, được bật mặc định trong Kafka 4.0:

Đặc điểmEager Rebalance (cũ)KIP-848 Protocol (mới)
Khi consumer mới joinTất cả revoke, reassign lạiChỉ di chuyển partition cần thiết
Khi consumer rời groupStop-the-worldIncremental, không gián đoạn
Assignment logicClient-side (group leader)Server-side (broker quyết định)
Thời gian rebalanceTỷ lệ thuận với số consumerGần như không đổi (O(1))
Downtime khi scaleCó, đáng kểGần như bằng 0

Để kích hoạt trên client, chỉ cần set:

group.protocol=consumer

4. Eligible Leader Replicas — Không còn mất dữ liệu khi bầu Leader

KIP-966 giới thiệu khái niệm Eligible Leader Replicas (ELR) — một tập con của ISR (In-Sync Replicas) được đảm bảo có đầy đủ dữ liệu đến high-watermark. Trong trường hợp leader partition gặp sự cố, Kafka sẽ chỉ bầu leader mới từ các ELR, ngăn chặn việc một replica chưa đồng bộ hoàn toàn trở thành leader và gây mất dữ liệu.

sequenceDiagram
    participant P as Producer
    participant L as Leader Broker
    participant R1 as Replica 1 (ELR)
    participant R2 as Replica 2 (Non-ELR)

    P->>L: Produce message (offset 100)
    L->>R1: Replicate (offset 100) ✓
    L->>R2: Replicate (offset 95) — lag
    Note over L: High-watermark = 100
    Note over R1: Caught up → ELR ✓
    Note over R2: Lagging → NOT ELR ✗
    L--xL: Leader crashes!
    Note over R1,R2: Leader election
    R1->>R1: Elected as new leader (ELR, no data loss)
    R2--xR2: Rejected (not in ELR, would lose offsets 96-100)

ELR đảm bảo chỉ replica đã đồng bộ hoàn toàn mới được bầu làm leader

5. Kafka Streams 4.2: Dead Letter Queue & Server-Side Rebalance

Kafka Streams trong phiên bản 4.2 nhận được nhiều cải tiến quan trọng cho production:

Dead Letter Queue (DLQ) tích hợp

Trước 4.2, khi một record gây lỗi trong Kafka Streams topology, bạn chỉ có 2 lựa chọn: skip record hoặc crash ứng dụng. Giờ đây, exception handler có thể redirect record lỗi sang Dead Letter Queue — một topic riêng để phân tích và xử lý sau.

// Kafka Streams 4.2 — DLQ trong exception handler
StreamsConfig config = new StreamsConfig();
config.put(StreamsConfig.PROCESSING_EXCEPTION_HANDLER_CLASS_CONFIG,
    DeadLetterQueueExceptionHandler.class);
config.put("dead.letter.queue.topic", "order-processing-dlq");

Server-Side Rebalance Protocol (GA)

Kafka Streams giờ sử dụng broker-side task assignment thay vì client-side, giảm đáng kể độ phức tạp và thời gian rebalance khi scale ứng dụng stream processing.

Anchored Wall-Clock Punctuation

Tính năng mới cho phép schedule punctuation tại thời điểm cố định (ví dụ: đầu mỗi giờ, đầu mỗi ngày) thay vì chỉ dựa trên interval. Hữu ích cho các tác vụ aggregation theo lịch cố định.

6. Tích hợp Kafka 4.x với .NET

Thư viện chính thức Confluent.Kafka (phiên bản 2.14.0) cung cấp high-level Producer và Consumer tương thích đầy đủ với Kafka 4.x. Dưới đây là các patterns production-ready.

Producer với Idempotent Delivery

using Confluent.Kafka;

var config = new ProducerConfig
{
    BootstrapServers = "kafka-cluster:9092",
    EnableIdempotence = true,
    Acks = Acks.All,
    MessageSendMaxRetries = 3,
    LingerMs = 5,
    BatchSize = 65536
};

using var producer = new ProducerBuilder<string, string>(config)
    .SetErrorHandler((_, e) =>
        Console.Error.WriteLine($"Kafka error: {e.Reason}"))
    .Build();

var result = await producer.ProduceAsync("order-events",
    new Message<string, string>
    {
        Key = orderId,
        Value = JsonSerializer.Serialize(orderEvent),
        Headers = new Headers
        {
            { "correlation-id", Encoding.UTF8.GetBytes(correlationId) },
            { "event-type", Encoding.UTF8.GetBytes("OrderCreated") }
        }
    });

Console.WriteLine($"Delivered to {result.TopicPartitionOffset}");

Consumer với Manual Offset

var config = new ConsumerConfig
{
    BootstrapServers = "kafka-cluster:9092",
    GroupId = "order-processor",
    GroupProtocol = "consumer",  // KIP-848 protocol mới
    AutoOffsetReset = AutoOffsetReset.Earliest,
    EnableAutoCommit = false,
    MaxPollIntervalMs = 300000
};

using var consumer = new ConsumerBuilder<string, string>(config)
    .SetPartitionsAssignedHandler((c, partitions) =>
        Console.WriteLine($"Assigned: {string.Join(", ", partitions)}"))
    .Build();

consumer.Subscribe("order-events");

while (!cancellationToken.IsCancellationRequested)
{
    var result = consumer.Consume(cancellationToken);
    try
    {
        await ProcessOrderEvent(result.Message);
        consumer.StoreOffset(result);
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "Failed to process {Key}", result.Message.Key);
        await PublishToDlq(result.Message);
        consumer.StoreOffset(result);
    }
}

Tích hợp với .NET Aspire

Package Aspire.Confluent.Kafka cung cấp dependency injection, health checks, và telemetry tự động:

// Program.cs — .NET Aspire host
var builder = DistributedApplication.CreateBuilder(args);

var kafka = builder.AddKafka("messaging")
    .WithKRaft()    // Sử dụng KRaft mode
    .WithDataVolume();

var orderService = builder.AddProject<Projects.OrderService>()
    .WithReference(kafka);

// OrderService — DI registration
builder.AddKafkaProducer<string, OrderEvent>("messaging");
builder.AddKafkaConsumer<string, OrderEvent>("messaging", settings =>
{
    settings.Config.GroupId = "order-processor";
    settings.Config.GroupProtocol = "consumer";
});

7. Kiến trúc Event-Driven hoàn chỉnh với Kafka 4.x

Kafka 4.x với Share Groups mở ra khả năng xây dựng kiến trúc event-driven linh hoạt hơn, kết hợp cả pub/sub và point-to-point messaging trên cùng một platform:

graph TD
    API[API Gateway] --> CMD[Command Service]
    API --> QRY[Query Service]

    CMD --> KT1[Topic: domain-events
Consumer Group — ordered] CMD --> KT2[Topic: task-queue
Share Group — parallel] KT1 --> ES[Event Store Service] KT1 --> PROJ[Projection Service] KT1 --> NOTIFY[Notification Service] KT2 --> W1[Worker 1] KT2 --> W2[Worker 2] KT2 --> W3[Worker 3] ES --> DB1[(Event Store)] PROJ --> DB2[(Read DB)] QRY --> DB2 W1 --> EXT[External APIs] W2 --> EXT W3 --> EXT style API fill:#2c3e50,stroke:#fff,color:#fff style CMD fill:#e94560,stroke:#fff,color:#fff style QRY fill:#e94560,stroke:#fff,color:#fff style KT1 fill:#4CAF50,stroke:#fff,color:#fff style KT2 fill:#ff9800,stroke:#fff,color:#fff style ES fill:#f8f9fa,stroke:#e94560,color:#333 style PROJ fill:#f8f9fa,stroke:#e94560,color:#333 style NOTIFY fill:#f8f9fa,stroke:#e94560,color:#333 style W1 fill:#f8f9fa,stroke:#ff9800,color:#333 style W2 fill:#f8f9fa,stroke:#ff9800,color:#333 style W3 fill:#f8f9fa,stroke:#ff9800,color:#333 style DB1 fill:#2c3e50,stroke:#fff,color:#fff style DB2 fill:#2c3e50,stroke:#fff,color:#fff style EXT fill:#9e9e9e,stroke:#fff,color:#fff

Kiến trúc kết hợp Consumer Group (ordered events) và Share Group (parallel tasks)

Khi nào dùng Consumer Group vs Share Group?

  • Consumer Group: Domain events, CDC streams, event sourcing — nơi thứ tự xử lý quan trọng
  • Share Group: Email gửi, image processing, report generation, API calls — nơi cần scale workers linh hoạt

8. Migration từ ZooKeeper sang KRaft

Nếu bạn đang chạy Kafka cluster với ZooKeeper, quy trình migration sang KRaft bao gồm 3 bước chính:

Bước 1: Chuẩn bị
Nâng cấp toàn bộ brokers lên Kafka 3.7+ (phiên bản cuối hỗ trợ cả hai mode). Đảm bảo tất cả clients tương thích. Backup metadata và cấu hình ZooKeeper. Chạy kafka-features.sh describe để kiểm tra feature levels.
Bước 2: Migration
Chạy kafka-metadata.sh snapshot để chuyển metadata từ ZooKeeper sang KRaft format. Khởi động KRaft controllers. Chuyển từng broker sang KRaft mode với rolling restart. Verify metadata consistency giữa ZK và KRaft.
Bước 3: Hoàn tất
Sau khi tất cả brokers chạy KRaft, ngắt kết nối ZooKeeper. Xóa cấu hình zookeeper.connect khỏi broker configs. Decommission ZooKeeper ensemble. Monitor cluster health trong 48-72 giờ trước khi xóa ZK nodes.

Lưu ý quan trọng khi migration

Kafka 4.0+ không còn hỗ trợ ZooKeeper mode. Nếu cluster đang chạy ZooKeeper, bạn phải migration sang KRaft trước khi nâng cấp lên 4.x. Thực hiện migration trên phiên bản 3.7 hoặc 3.8, sau đó mới upgrade lên 4.x.

9. Kafka 4.x so với các Messaging Systems khác

Tiêu chíKafka 4.xRabbitMQAWS SQS/SNS
Mô hìnhPub/Sub + Queue (Share Groups)Queue + Pub/Sub (Exchange)Queue (SQS) + Pub/Sub (SNS)
ThroughputHàng triệu msg/s~50K msg/sGần như unlimited (managed)
Message retentionConfigurable (ngày/tuần/vĩnh viễn)Đến khi consumed14 ngày (SQS)
OrderingPer-partition guaranteedPer-queue FIFOFIFO queue hoặc best-effort
Consumer scalingConsumer Group + Share GroupCompeting consumersAuto-scaling consumers
Stream processingKafka Streams, ksqlDBKhông tích hợpCần Lambda/Kinesis
Vận hànhSelf-managed hoặc Confluent CloudSelf-managed hoặc CloudAMQPFully managed
Chi phíInfra cost (hoặc Confluent pricing)Infra costPay-per-request

10. Best Practices cho Kafka Production

Sizing & Performance

  • Partition count: Bắt đầu với số partitions = số consumers dự kiến × 2. Tăng partition dễ, giảm thì không
  • Replication factor: Luôn đặt ít nhất 3 cho production. Kết hợp min.insync.replicas=2 với acks=all
  • Batch size: Tăng batch.size (64KB-256KB) và linger.ms (5-20ms) để cải thiện throughput
  • Compression: Sử dụng compression.type=zstd cho tỷ lệ nén tốt nhất hoặc lz4 cho latency thấp nhất

Monitoring essentials

  • Consumer lag: Metric quan trọng nhất — nếu lag tăng liên tục, consumer không theo kịp producer
  • Under-replicated partitions: Báo hiệu broker gặp vấn đề I/O hoặc network
  • Request latency (p99): Theo dõi produce và fetch latency ở percentile 99
  • Controller active count: Trong KRaft, luôn phải có đúng 1 active controller

Security

  • Sử dụng SASL/SCRAM hoặc mTLS cho authentication
  • Bật TLS encryption cho mọi kết nối inter-broker và client-broker
  • Cấu hình ACLs chi tiết per-topic, per-consumer-group
  • Với KRaft, chỉ cần quản lý một hệ thống ACL duy nhất (không còn ZooKeeper ACLs riêng)

Kết luận

Apache Kafka 4.x đánh dấu bước chuyển mình lớn nhất trong lịch sử của nền tảng event streaming phổ biến nhất thế giới. Việc loại bỏ ZooKeeper không chỉ đơn giản hóa vận hành mà còn mở ra khả năng scale đến hàng triệu partitions. Share Groups lấp đầy khoảng trống lớn nhất của Kafka — khả năng hoạt động như message queue — khiến nhiều hệ thống không còn cần chạy song song cả Kafka lẫn RabbitMQ. Với ecosystem .NET ngày càng hoàn thiện qua Confluent.Kafka và .NET Aspire, đây là thời điểm lý tưởng để đưa Kafka vào kiến trúc event-driven của bạn.

Tham khảo