# =============================================================================
# kraft_config_example.properties
# -----------------------------------------------------------------------------
# Kafka KRaft 模式配置范例 —— 一份文件覆盖三种典型部署：
#
#   ① Combined 模式：单进程同时扮演 broker + controller   （学习 / 单机 / 小集群）
#   ② Controller-only：只做 Controller，不接客户端读写    （生产 Quorum 节点）
#   ③ Broker-only：只做 Broker，不参与 metadata Quorum    （生产业务节点）
#
# 用法：把下面对应模式的小节复制成 server.properties，按需修改 IP/端口/路径，
# 然后用 kafka-storage.sh format 初始化，再 kafka-server-start.sh 启动。
#
# 第一次启动前必须 format（KRaft 与 ZK 时代最大的区别之一）：
#
#   # 在「集群里随便一台」生成 cluster ID
#   CLUSTER_ID=$(/opt/kafka/bin/kafka-storage.sh random-uuid)
#   echo $CLUSTER_ID
#
#   # 然后在「每个节点」上用同一个 ID format
#   /opt/kafka/bin/kafka-storage.sh format \
#       --config /opt/kafka/config/kraft/server.properties \
#       --cluster-id $CLUSTER_ID
#
# 启动：
#   /opt/kafka/bin/kafka-server-start.sh /opt/kafka/config/kraft/server.properties
# =============================================================================


# =============================================================================
# 模式 ① Combined（broker + controller 同进程）  —— 学习 / 小集群
# =============================================================================
process.roles=broker,controller

# 节点全局唯一 ID（KRaft 时代相当于 broker.id）
node.id=1

# Quorum 投票成员清单：写法 nodeId@host:port  (port 是 controller 端口)
# 三个节点的这一行配置必须**完全一致**，包括 host:port、id
controller.quorum.voters=1@kafka1:9093,2@kafka2:9093,3@kafka3:9093

# Quorum 内部使用哪个 listener 通信（必须出现在 listeners 里）
controller.listener.names=CONTROLLER

# 监听器：CONTROLLER 内部 RPC + PLAINTEXT 客户端入口
listeners=PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093
advertised.listeners=PLAINTEXT://kafka1:9092
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
inter.broker.listener.name=PLAINTEXT

# 数据与元数据存放目录
log.dirs=/var/lib/kafka/data
metadata.log.dir=/var/lib/kafka/data

# 内部 Topic 副本数（与 Broker 数对齐，生产至少 3）
offsets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=2
min.insync.replicas=2

# 教学环境关闭自动建 Topic，强制脚本/UI 显式创建
auto.create.topics.enable=false


# =============================================================================
# 模式 ② Controller-only （生产推荐：3 ~ 5 个独立 Controller 节点）
# -----------------------------------------------------------------------------
# 这种节点只跑 Raft Quorum，不接客户端读写，不存数据 Topic 副本。
# 把整个集群的「大脑」与「血管」物理隔离，避免业务流量影响选举与元数据同步。
# =============================================================================
# process.roles=controller
# node.id=3001
# controller.quorum.voters=3001@ctrl1:9093,3002@ctrl2:9093,3003@ctrl3:9093
# controller.listener.names=CONTROLLER
# listeners=CONTROLLER://0.0.0.0:9093
# # 注意：controller-only 节点不需要 advertised.listeners 给客户端
# # 也不需要 inter.broker.listener.name
# log.dirs=/var/lib/kafka/metadata
# metadata.log.dir=/var/lib/kafka/metadata
#
# # Controller 节点不参与业务数据，但仍然要为元数据日志预留磁盘
# # 推荐独立 SSD，至少 50GB
# metadata.log.segment.bytes=1073741824
# metadata.log.max.snapshot.interval.ms=3600000


# =============================================================================
# 模式 ③ Broker-only （生产推荐：N 个 Broker 节点，N 与业务规模相关）
# -----------------------------------------------------------------------------
# 这种节点专门跑数据 Topic 的 Leader/Follower，不参与 Quorum 投票。
# 它们通过 controller.quorum.voters 找到 Controller Quorum，注册自己、
# fetch __cluster_metadata 来获取元数据视图。
# =============================================================================
# process.roles=broker
# node.id=101
# controller.quorum.voters=3001@ctrl1:9093,3002@ctrl2:9093,3003@ctrl3:9093
# controller.listener.names=CONTROLLER
#
# listeners=PLAINTEXT://0.0.0.0:9092,INTERNAL://0.0.0.0:19092
# advertised.listeners=PLAINTEXT://broker-101.example.com:9092,INTERNAL://broker-101.internal:19092
# listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,INTERNAL:PLAINTEXT
# inter.broker.listener.name=INTERNAL
#
# log.dirs=/data/kafka/log1,/data/kafka/log2     # 多盘并行
#
# # 业务数据相关
# num.partitions=3
# default.replication.factor=3
# min.insync.replicas=2
# unclean.leader.election.enable=false
# offsets.topic.replication.factor=3
# transaction.state.log.replication.factor=3
# transaction.state.log.min.isr=2
#
# # 副本相关
# num.replica.fetchers=4                  # 多线程拉副本
# replica.lag.time.max.ms=30000


# =============================================================================
# KRaft 通用调优（所有模式都可参考）
# =============================================================================
# 元数据快照：太频繁占 IO，太稀疏重启慢，1 小时 + 100MB 是一个平衡点
# metadata.log.max.snapshot.interval.ms=3600000
# metadata.log.segment.bytes=1073741824

# Quorum 选举超时：默认 1s。网络抖动较大的集群可调到 2~5s 减少误选举
# controller.quorum.election.timeout.ms=1000
# controller.quorum.fetch.timeout.ms=2000

# 元数据请求超时（普通 Broker 端使用）
# controller.socket.timeout.ms=30000

# 日志保留 / 清理
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000

# 网络与线程
num.network.threads=3
num.io.threads=8
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600

# JVM Heap：学习 1G，小集群 4G，大集群 6~8G
# 用 KAFKA_HEAP_OPTS="-Xms6G -Xmx6G" 在启动脚本里设置，不要写本文件


# =============================================================================
# 常见错误自检
# =============================================================================
# 1) 启动报 InconsistentClusterIdException：
#    每个节点的 meta.properties 里的 cluster.id 必须相同。
#    解决：rm -rf $log.dirs/meta.properties，重新 kafka-storage.sh format。
#
# 2) 启动报 InconsistentNodeIdException：
#    本节点 node.id 与 meta.properties 中记录的 node.id 不一致。
#    解决：要么修配置，要么删 meta.properties 重新 format。
#
# 3) 启动卡在「Awaiting socket connection to controller」：
#    controller.quorum.voters 配错；先 ping 通 host:port，再确认 voters 三节点完全一致。
#
# 4) Quorum 选不出 Leader：
#    多数派 = floor(N/2)+1。N=3 至少要 2 个节点活着。
#    用 kafka-metadata-quorum.sh describe --status 看当前 Voter 状态。
#
# 5) Active Controller 频繁切换：
#    可能 GC pause 太久（>1s 触发 election timeout），调大
#    controller.quorum.election.timeout.ms 或迁出业务流量。
# =============================================================================
