Kafka with Kraft mode
Last updated
Last updated
ZooKeeper dường như là một phần không thể tách rời khi vận hành một hệ thống phân tán như Kafka, nhưng nó có một số hạn chế khiến Kafka không thể phát huy hết khả năng của mình. Khi quy mô Kafka tăng lên, hệ thống gặp phải bottleneck về hiệu suất do sự phụ thuộc vào ZooKeeper.
Kafka metadata bao gồm thông tin về các broker, thông tin máy chủ, cổng kết nối và vị trí lưu trữ của các partition. Trong kiến trúc cũ, ZooKeeper giữ vai trò trung tâm trong việc quản lý metadata của Kafka. Zookeeper Watchers sẽ chịu trách nhiệm bầu chọn ra Kafka Controller.
Kafka Controller: Là node đóng vai trò điều khiển trong hệ thống Kafka, chịu trách nhiệm duy trì metadata và điều phối cluster. Từ đây bắt đầu xảy ra vấn đề, khi Lưu lượng metadata lớn dần lên theo thời gian phần lớn lưu lượng đọc và ghi metadata được thực hiện bởi controller node, dẫn đến bottleneck. Khi một node Kafka bị lỗi hoặc bị tắt, controller cần ghi lại metadata mới vào ZooKeeper và cập nhật metadata cho từng broker theo thứ tự. Trong lúc này, client có thể không truy vấn được leader của partition do thông tin metadata chưa được cập nhật kịp thời.
Việc triển khai một cluster Kafka phụ thuộc vào ZooKeeper – một hệ thống độc lập với Kafka, đi kèm với tập lệnh điều khiển và cấu hình riêng. Điều này làm tăng độ phức tạp khi triển khai, giám sát và quản lý hệ thống. Việc quản lý và bảo mật 2 hệ thống phức tạp hơn nhiều so với chỉ 1.
Khi khởi động cluster, Kafka Controller phải tải trạng thái cluster từ ZooKeeper. Điều tương tự cũng xảy ra khi một broker tham gia hoặc rời khỏi cluster, khiến ZooKeeper thực hiện lại quá trình bầu chọn leader. Khi số lượng metadata ngày càng lớn, việc tải lại metadata trở nên kém hiệu quả, hạn chế số lượng partition mà cluster có thể hỗ trợ (theo Confluent, giới hạn này vào khoảng 200.000 partition).
Để giải quyết vấn đề này thì một KIP (Kafka Improvement Plan) đã được đưa ra là . Cụ thể Zookeeper có thể được thay thể bằng Kraft Mode (Kafka Raft). Điều này được tử nghiệm từ phiên bản 2.8.0 và đã chính thức release phiên bản 4.0.0 vào 18/03/2025. Bài viết cụ thể tại đây .
Cơ chế đồng thuận Raft là một thuật toán đồng thuận phân tán được thiết kế để đảm bảo rằng các máy tính trong một hệ thống phân tán có thể thống nhất trạng thái của chúng, ngay cả khi một số máy gặp sự cố.
Raft giải quyết vấn đề đồng thuận bằng cách chia quá trình thành các bước rõ ràng và dễ hiểu. Trong một hệ thống phân tán, các máy tính (gọi là node) cần đồng ý về một chuỗi các lệnh hoặc giá trị (ví dụ: cập nhật dữ liệu). Raft đạt được điều này thông qua:
Chọn một leader: Một node sẽ được bầu làm leader để điều phối các quyết định.
Nhật ký sao chép (log replication): Leader nhận các lệnh từ client, ghi chúng vào nhật ký của mình, và sau đó sao chép chúng đến các node khác (follower).
Raft đảm bảo rằng:
Mọi node đều có cùng một chuỗi nhật ký (log) theo thời gian.
Nếu một node gặp sự cố, hệ thống vẫn có thể tiếp tục hoạt động (fault tolerance).
Leader: Node lãnh đạo, chịu trách nhiệm tiếp nhận yêu cầu từ client, quản lý nhật ký, và sao chép chúng đến các follower. Chỉ có một leader duy nhất tại một thời điểm.
Follower: Node thụ động, chỉ nhận và sao chép nhật ký từ leader. Follower không tự đưa ra quyết định mà chỉ làm theo leader.
Candidate: Trạng thái trung gian khi một node đang cố gắng trở thành leader trong quá trình bầu cử.
Các node chuyển đổi giữa các trạng thái này dựa trên tình hình của hệ thống (ví dụ: leader bị lỗi, hoặc hết thời gian chờ).
Term (Nhiệm kỳ): Mỗi khoảng thời gian mà một leader điều hành hệ thống được gọi là một term. Term được đánh số nguyên tăng dần (1, 2, 3, ...).
Log (nhật ký): Một chuỗi các mục (log entries) chứa các lệnh từ client, được lưu trữ theo thứ tự thời gian. Mỗi mục bao gồm: chỉ số (index), term, và giá trị (dữ liệu).
Commit (Xác nhận): Một mục nhật ký được coi là "committed" khi nó đã được sao chép thành công lên đa số node (quorum).
Quorum (Đa số): Số lượng tối thiểu các node cần đồng ý để một quyết định (bầu cử hoặc commit) được thông qua. Thường là hơn 50% tổng số node.
Election Timeout: Khoảng thời gian ngẫu nhiên (thường từ 150ms - 300ms) mà một follower chờ trước khi quyết định leader đã bị lỗi và chuyển sang trạng thái candidate để bắt đầu bầu cử. Nếu hết thời gian này mà chưa nhận được AppendEntries thì Node sẽ chuyển trạng thái sang candidate và kêu gọi vote.
AppendEntries: Tin nhắn mà leader gửi đến follower để sao chép các mục nhật ký hoặc gửi heartbeat (tin nhắn giữ liên lạc để biết leader vẫn còn).
Split Vote: Tình huống khi không candidate nào nhận được đa số phiếu trong một cuộc bầu cử do phiếu bị chia đều. Raft giảm thiểu split vote bằng cách sử dụng election timeout ngẫu nhiên, giúp các node bắt đầu bầu cử ở thời điểm khác nhau.
Heartbeat: Tin nhắn định kỳ mà leader gửi đến follower (thông qua AppendEntries) để thông báo rằng nó vẫn hoạt động.
Raft chia quá trình đồng thuận thành ba phần chính:
Mỗi node bắt đầu là một follower và chờ tin nhắn từ leader.
Mỗi leader hoạt động trong một term (nhiệm kỳ), được đánh số tăng dần (term 1, term 2, ...).
Nếu một follower không nhận được tin nhắn từ leader trong một khoảng thời gian ngẫu nhiên (election timeout), nó sẽ chuyển thành candidate và bắt đầu một cuộc bầu cử:
Candidate tăng số term của mình lên.
Gửi yêu cầu bỏ phiếu (RequestVote) đến tất cả các node khác.
Nếu nhận được đa số phiếu (quorum, thường là hơn 50% số node), candidate trở thành leader.
Nếu không có ai giành được đa số, cuộc bầu cử thất bại và quá trình bắt đầu lại với term mới.
Khi đã có leader, mọi yêu cầu từ client (ví dụ: "ghi giá trị X") sẽ được gửi đến leader.
Leader ghi yêu cầu này vào nhật ký của mình dưới dạng một mục nhật ký (log entry), bao gồm:
Giá trị (dữ liệu từ client).
Term hiện tại.
Chỉ số (index) để xác định thứ tự trong nhật ký.
Leader gửi mục nhật ký này đến tất cả các follower qua tin nhắn AppendEntries.
Follower nhận tin nhắn, thêm mục vào nhật ký của mình, và gửi xác nhận lại cho leader.
Khi đa số follower xác nhận, leader commit (xác nhận) mục nhật ký này và thông báo cho follower để áp dụng nó vào trạng thái của hệ thống (state machine).
Raft đảm bảo rằng nhật ký của các node luôn nhất quán bằng cách:
Leader chỉ commit một mục khi đa số node đã sao chép nó.
Nếu leader bị lỗi, leader mới được bầu sẽ tiếp tục từ điểm mà leader cũ dừng lại, dựa trên nhật ký đã commit.
Raft sử dụng cơ chế kiểm tra tính nhất quán (consistency check) trong tin nhắn AppendEntries để đảm bảo follower không bị thiếu hoặc sai lệch nhật ký.
Trong chế độ KRaft, Kafka sử dụng một quorum controller (nhóm điều khiển) để quản lý metadata và đảm bảo tính nhất quán trong cluster. Dưới đây là cách hoạt động chính:
Một nhóm nhỏ các broker (thường là 3, 5 hoặc số lẻ) được chọn làm controller.
Các controller này chạy thuật toán Raft để:
Lưu trữ và đồng bộ metadata trong một log đặc biệt gọi là metadata log.
Bầu chọn leader trong số các controller nếu một node bị lỗi.
Chỉ một controller là active (hoạt động) tại một thời điểm (gọi là leader), các controller khác là standby (dự phòng).
Leader chịu trách nhiệm ghi các sự kiện (event) liên quan đến metadata vào một metadata topic (thực chất là một log nội bộ của Kafka).
Các standby controller lắng nghe và sao chép (replicate) các event từ metadata topic mà Leader ghi vào, giữ một bản sao metadata trong bộ nhớ của mình.
Metadata topic là nơi lưu trữ tất cả các thay đổi về trạng thái cluster (ví dụ: tạo topic, thay đổi leader partition, trạng thái broker). Đây là một log phân tán, được sao chép giữa các controller trong quorum.
Thay vì lưu metadata trong ZooKeeper, KRaft lưu nó trong một log nội bộ của Kafka (giống như cách Kafka lưu trữ message trong topic).
Log này được sao chép (replicated) giữa các controller để đảm bảo tính sẵn sàng cao (high availability).
Trong chế độ KRaft, các broker có thể đảm nhận hai vai trò:
Controller: Quản lý metadata và điều phối cluster.
Broker thông thường: Xử lý dữ liệu từ producer và consumer.
Một broker có thể vừa là controller vừa xử lý dữ liệu, nhưng trong thực tế, bạn thường tách riêng để tối ưu hiệu suất.
Đơn giản hóa kiến trúc:
Không cần cài đặt và quản lý ZooKeeper riêng.
Chỉ cần triển khai Kafka là đủ.
Hiệu suất tốt hơn
Giảm độ trễ vì không cần giao tiếp qua một hệ thống bên ngoài.
Có thể xử lý hàng triệu partition trong một cluster mà không bị giới hạn bởi ZooKeeper.
Khả năng mở rộng
KRaft hỗ trợ các cluster lớn hơn với số lượng partition lớn hơn.
Dễ bảo trì
Ít thành phần hơn, giảm nguy cơ lỗi và công sức vận hành.
💡Câu hỏi: Quorum Controller có đảm nhận công việc như broker thông thường không?
Trong Apache Kafka với chế độ KRaft, câu hỏi liệu Quorum Controller có đảm nhận công việc như một broker thông thường hay không phụ thuộc vào cách bạn cấu hình cluster.
Vai trò của Quorum Controller và Broker
Quorum Controller:
Đây là một nhóm nhỏ các node (thường 3, 5, hoặc số lẻ) trong cluster Kafka chịu trách nhiệm quản lý metadata và điều phối hoạt động của cluster.
Chúng chạy thuật toán Raft để đảm bảo đồng thuận (consensus), lưu trữ metadata trong metadata log, và bầu chọn leader trong số các controller khi cần.
Vai trò chính: Điều hành cluster, không trực tiếp xử lý dữ liệu từ producer hoặc consumer.
Broker thông thường:
Các node này chịu trách nhiệm lưu trữ dữ liệu (partition), xử lý yêu cầu từ producer (ghi dữ liệu) và consumer (đọc dữ liệu).
Vai trò chính: Xử lý luồng dữ liệu thực tế.
Trong KRaft mode, Kafka cho phép một node đảm nhận cả hai vai trò (controller và broker) hoặc tách biệt chúng, tùy thuộc vào cấu hình:
Cấu hình kết hợp (Combined Role): Một node vừa là controller vừa là broker.
Trong trường hợp này, node đó sẽ:
Quản lý metadata (vai trò controller).
Đồng thời xử lý dữ liệu từ producer và consumer (vai trò broker).
Phù hợp với: Các cluster nhỏ hoặc môi trường thử nghiệm, nơi bạn muốn tiết kiệm tài nguyên.
Cấu hình tách biệt (Separated Role): Một số node chỉ làm controller (process.roles=controller), còn các node khác chỉ làm broker (process.roles=broker).
Trong trường hợp này, Quorum Controller không đảm nhận công việc của broker thông thường, mà chỉ tập trung vào quản lý metadata và điều phối.
Phù hợp với: Các cluster lớn, production environment, nơi cần tối ưu hiệu suất và độ tin cậy.
Có thể xem minh hoạ chi tiết hơn tại
Cách cấu hình: Trong file , bạn đặt process.roles=broker,controller cho node đó.