分享程式代碼相關筆記
目前文章總數:182 篇
最後更新:2025年 05月 31日
Kafka 從 3.8.0 開始,可以獨立使用 KRaft 並且不再使用 Zookeeper 做集群管理 Kraft 模式官網
官方有以下概述:
Apache Kafka Raft (KRaft) is the consensus protocol that was introduced in KIP-500 to remove Apache Kafka’s dependency on ZooKeeper for metadata management. This greatly simplifies Kafka’s architecture by consolidating responsibility for metadata into Kafka itself, rather than splitting it between two different systems: ZooKeeper and Kafka. KRaft mode makes use of a new quorum controller service in Kafka which replaces the previous controller and makes use of an event-based variant of the Raft consensus protocol.
簡要的說:簡化了 Kafka 的架構。KRaft 模式利用 Kafka 中的新仲裁控制器服務來取代先前的控制器。
徹底改變了 Kafka 的架構基礎,使其成為更自包含、更有效率的分散式系統
關於 Kraft 整理以下特性:
1. KRaft 的本質
1. 基於 Raft 的共識演算法 | 採用了 Raft 共識演算法來確保元資料在多個節點間的一致性。 Raft 以其簡單易懂的特性而聞名,相比其他共識演算法更容易實現和維護。 |
2. 元資料主題 | KRaft 使用特殊的內部主題(__cluster_metadata)來儲存所有叢集元數據,就像普通的 Kafka 主題一樣,但具有特殊的處理方式 |
2. KRaft 的核心組件
1. 控制器(Controller) | 負責處理元資料操作 |
維護集群狀態 | |
處理領導者選舉 | |
可獨立運作或與 Broker 角色共存 | |
2. Quorum 控制器 | 多個控制器節點組成一個 Quorum(法定人數集合) |
透過 Raft 協定在控制器之間複製元數據 | |
確保元資料的高可用性和一致性 | |
3. Active 控制器 | 在任何時刻,只有一個控制器是活躍的(Active Controller) |
活躍控制器負責處理所有元資料請求 | |
如果活躍控制器失效,Quorum 會自動選舉新的活躍控制器 |
3. KRaft 提供 Kafka 新的部署模式
1. Combined 模式 | 節點同時擔任控制器和 Broker 角色 |
適用於小型到中型集群 | |
資源利用率更高 | |
2. Separated 模式 | 控制器和 Broker 角色分離到不同節點 |
適用於大型生產環境 | |
提供更好的隔離性和可維護性 |
4. KRaft 讓 Kafka 變得更高效能
1. 高效能元資料操作 | 減少元資料操作的網路跳躍 |
更新資料延遲更低 | |
2. 簡化的架構 | 單一系統,不依賴外部組件(不依賴 Zookeeper) |
統一的配置、監控和日誌處理 | |
3. 可擴展性 | 支援更大規模的主題和分區數量 |
更有效率的元資料管理演算法 |
特性 | ZooKeeper 架構 | KRaft 架構 |
---|---|---|
架構組成 | 雙組件:Kafka Broker + 外部 ZooKeeper 集群 | 單組件:只需 Kafka 伺服器(可分為控制器和 Broker 角色) |
元數據存儲 | 存儲在 ZooKeeper 中,非同步複製到 Broker | 直接存儲在 Kafka 內部的元數據主題中 |
一致性協議 | ZAB (ZooKeeper Atomic Broadcast) | Raft 共識協議 |
擴展性 | 支援約幾千個分區,大規模集群下性能受限 | 可支援數萬至十萬級分區數量 |
性能 | 元數據操作需跨系統通信,有額外延遲 | 元數據變更更快,直接在 Kafka 內處理 |
運維複雜度 | 需維護兩套系統,配置和監控更複雜 | 單一系統,簡化部署和維護 |
故障排查 | 需檢查兩個系統的日誌和狀態 | 只需檢查 Kafka 系統 |
資源消耗 | 需要額外的伺服器資源運行 ZooKeeper | 可以複用現有 Kafka 資源,降低總體消耗 |
啟動流程 | Kafka 依賴 ZooKeeper 先啟動 | 直接啟動 Kafka 即可 |
配置管理 | 需要管理兩套配置 | 只需管理 Kafka 配置 |
少了 ZooKeeper 可以提升整體效能,並且簡化架構
建議使用 Kafka 3.8.0 版本以上,因為 Kraft 穩定(Release)、成熟、並且從這版本計畫移除 ZooKeeper
Kafka 版本 | ZooKeeper 支援 | KRaft 支援 | 備註 |
---|---|---|---|
< 2.8.0 | 必需 | 不支援 | 只能使用 ZooKeeper 模式 |
2.8.0 | 必需 | 實驗性支援 | KRaft 首次引入,僅用於測試 |
3.0.0 | 支援 | 預覽版 | KRaft 可用於有限生產使用 |
3.1.0 - 3.2.x | 支援 | 改進版預覽 | KRaft 穩定性顯著提升 |
3.3.0 - 3.4.x | 支援 | 生產就緒 | KRaft 開始推薦用於生產環境 |
3.5.0+ | 支援但不推薦 | 完全支援 | KRaft 成為推薦部署方式 |
3.8.0 (您的版本) | 支援但不推薦 | 完全成熟 | KRaft 是首選模式,ZooKeeper 模式計劃逐步淘汰 |
4.0.0 (預計) | 可能移除 | 標準模式 | 可能完全移除 ZooKeeper 依賴 |
使用的 docker-compose.yml 有以下腳本,分別實現以下 2 個項目的容器化安裝:
項目 | 項目 |
---|---|
kafka | 安裝 3 個容器 Kafka 0 ~ 2 |
PLAINTEXT://0.0.0.0:9092 container 內部互連用 (9092, 9094, 9096) | |
PLAINTEXT://host.docker.internal:9093(或外部 IP) 給宿主機或外部服務用(9093, 9095, 9097) | |
kafka-ui | 提供 kafka 介面化檢視、操作;對外的 Web 操作是開放 8380 Port |
version: '3'
services:
kafka-0:
image: bitnami/kafka:3.8
container_name: kafka-0
ports:
- "9092:9092"
- "9093:9093"
environment:
- KAFKA_CFG_NODE_ID=0
- KAFKA_CFG_PROCESS_ROLES=broker,controller
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka-0:9093,1@kafka-1:9093,2@kafka-2:9093
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.51.28:9092
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT
- KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true
- KAFKA_CFG_NUM_PARTITIONS=3
- KAFKA_CFG_DEFAULT_REPLICATION_FACTOR=3
- KAFKA_CFG_MIN_INSYNC_REPLICAS=2
- KAFKA_KRAFT_CLUSTER_ID=abcdefghijklmnopqrstuv
volumes:
- kafka_data_0:/bitnami/kafka
restart: unless-stopped
kafka-1:
image: bitnami/kafka:3.8
container_name: kafka-1
ports:
- "9094:9092"
- "9095:9093"
environment:
- KAFKA_CFG_NODE_ID=1
- KAFKA_CFG_PROCESS_ROLES=broker,controller
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka-0:9093,1@kafka-1:9093,2@kafka-2:9093
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.51.28:9094
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT
- KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true
- KAFKA_CFG_NUM_PARTITIONS=3
- KAFKA_CFG_DEFAULT_REPLICATION_FACTOR=3
- KAFKA_CFG_MIN_INSYNC_REPLICAS=2
- KAFKA_KRAFT_CLUSTER_ID=abcdefghijklmnopqrstuv
volumes:
- kafka_data_1:/bitnami/kafka
restart: unless-stopped
kafka-2:
image: bitnami/kafka:3.8
container_name: kafka-2
ports:
- "9096:9092"
- "9097:9093"
environment:
- KAFKA_CFG_NODE_ID=2
- KAFKA_CFG_PROCESS_ROLES=broker,controller
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka-0:9093,1@kafka-1:9093,2@kafka-2:9093
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.51.28:9096
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT
- KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true
- KAFKA_CFG_NUM_PARTITIONS=3
- KAFKA_CFG_DEFAULT_REPLICATION_FACTOR=3
- KAFKA_CFG_MIN_INSYNC_REPLICAS=2
- KAFKA_KRAFT_CLUSTER_ID=abcdefghijklmnopqrstuv
volumes:
- kafka_data_2:/bitnami/kafka
restart: unless-stopped
kafka-ui:
image: provectuslabs/kafka-ui:latest
container_name: kafka-ui
ports:
- "8380:8080"
environment:
- KAFKA_CLUSTERS_0_NAME=local-kraft
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka-0:9092,kafka-1:9092,kafka-2:9092
depends_on:
- kafka-0
- kafka-1
- kafka-2
restart: unless-stopped
volumes:
kafka_data_0:
kafka_data_1:
kafka_data_2:
一、每個容器都有以下這段,目的是告訴 Kafka,哪些節點是 Controller(KRaft mode 下的 Raft quorum) 的投票成員。:
等於為 3 個容器(Id 0~2),設定了 3 個 KRaft Controller 的集群
KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.51.28:9096
二、每個容器都有以下這段,目的是設定該節點要開啟哪些通訊協議與端口:
KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
1. PLAINTEXT://:9092:開給 producer / consumer / kafka-ui 等客戶端用的通訊 port |
2. CONTROLLER://:9093:給 Controller 之間互相溝通(Raft 投票)的內部 port |
備註:9092 是 client-facing port,9093 是 Raft 通訊 port,不能搞混。 |
三、3 個容器都各自有自己的這段,目的是設定該節點要開啟哪些通訊協議與端口:
告知外部服務如果要連線,請走這個 listener
Kafka-0
KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.51.28:9092
Kafka-1
KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.51.28:9094
Kafka-2
KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.51.28:9096
1. PLAINTEXT://:9092:開給 producer / consumer / kafka-ui 等客戶端用的通訊 port |
2. CONTROLLER://:9093:給 Controller 之間互相溝通(Raft 投票)的內部 port |
備註:9092 是 client-facing port,9093 是 Raft 通訊 port,不能搞混。 |
至此會看到架構下會直接連線到 Kafka 對應容器,C# 程式中還需要配置 bootstrap server
將 docker-compose.yml 放到 Ubuntu 上
在對應根目錄下輸入 Shell 指令,進行安裝
docker-compose up -d
安裝完成後會出現 1 個 Kafka-UI 與 3 個 Kafka 0~2 容器。
DashBoard -> Brokers 同時運行的會有 3 個,當前為 Active 的為 Broker ID 0
輸入 Replicas 得到 2 的數量 (min.insync.replicas)
有 3 個 Kafka Brokers,而 Topic 的 副本因子(Replication Factor)設為 2
表示 即使 某 1 個 broker 掛掉,仍然有另一個副本能接手(高可用) :
1. Kafka 會確保每個 partition 有 1 個 leader + 1 個 follower |
2. 這 2 個副本會存在 不同的 broker 中 |
3. 占用空間量 3 個都活著的情況下,會變為 3 倍,因為副本 |
舉例來說,會看到以下的結構,在後續生產者產生資料到 Kafka 中:
※因此,單機模式下,有 2 個 Kafka(Broker) 容器 關閉,生產者就 100% 不能將資料送往 Kafka
※min.insync.replicas 設為 1 會失去高可用 & 資料一致性的問題,通常生產環境不會如此設定
Partition | Leader (Broker) | Follower (Broker) |
---|---|---|
0 | kafka-0 | kafka-1 |
1 | kafka-1 | kafka-2 |
2 | kafka-2 | kafka-0 |
啟動範例專案,進入 Debug 模式,嘗試發送
回到 DashBoard 可以發現有 Leader(Broker) 與 機器相關的資訊,成功發送
當前容器都是正常的狀況下,所有訊息都可以正常收到
當前架構下,如果沒有在 bootstrap 添加所有 Broker ,當發生 9092 關閉時,就會直接造成錯誤
這失去了副本因子的高可用
當前範例專案,設定檔只有配置 9092,單一容器
將 kafka-0 (Broker) Port:9092 關閉
可以發現產生生產者訊息時,會出現大量訪問異常,無法訪問,但實際上 Kafka-1 與 Kafka-2 是正常的
在 Confing 檔按下添加 Kafka-1 與 Kafka-2 容器位置
再次發送,並且成功發送,完成了高可用
這次再關閉一台容器,因為 min.insync.replicas = 2 ,預期不可發送
再次進行發送,雖然 Kafka-2 (Broker)活著,但沒有達到備份副本因子的最低要求 2 ,因此視為異常
即使 部分節點故障,仍能保證:
資料可寫入(只要副本同步夠)
資料可讀取(有 leader 存在)
故障節點恢復時,自動追上 missed 的資料(replica sync)