broker在zookeeper中存储的信息
-
/kafka/brokers/ids 记录了所有存活的broker服务器id
-
/kafka/brokers/topics/topic_name/partition/0/state 保存了关于该partition的leader信息和ISR信息
{
“controller_epoch” : 11,
“leader” : 0,
“version” : 1,
“leader_epoch” : 3,
“isr” : [ 0, 1 ]
} -
/kafka/controller 用于选举leader,谁先找到controller谁则竞选成功
broker工作流程
每个broker中都有controller ZK中也有/controller节点 作用在于leader选举和同步zk上注册的leader isr相关信息
-
broker启动向ZK注册,写入/broker/ids [0,1,2…]
-
/kafka/controller 选举leader 谁先注册到controller上就是leader
-
选举出来的Controller去监听brokers节点变化
-
ZK中的controller决定leader选举
-
controller将节点信息通知ZK /brokers/topics/xxx/partition/0/state 维护leader和isr信息
-
broker其他controller从ZK同步相关信息
-
此时producer的与集群中的leader进行通信,leader与其余follower之间再进行信息同步
-
如出现Broker中的leader宕机
-
发起重新选举
- 首先在ISR中存活为前提
- 再按照AR中排在前面的优先
- 例如AR[1,0,2],ISR[1,0,2]那么leader会按照1,0,2顺序来轮询
-
Controller监听节点的变化
-
重新获取ISR
-
选择新的leader(在ISR中存活为前提,按照AR中排在前面的优先)
-
更新leader和ISR
broker重要参数
参数名称 | 描述 |
---|---|
replica.lag.time.max.ms | ISR 中,如果 Follower 长时间未向 Leader 发送通 信请求或同步数据,则该 Follower 将被踢出 ISR。 该时间阈值,默认 30s。 |
auto.leader.rebalance.enable | 默认是 true。 自动 Leader Partition 平衡。 |
leader.imbalance.per.broker.percentage | 默认是 10%。每个 broker 允许的不平衡的 leader 的比率。如果每个 broker 超过了这个值,控制器 会触发 leader 的平衡。 |
leader.imbalance.check.interval.seconds | 默认值 300 秒。检查 leader 负载是否平衡的间隔时 间。 |
log.segment.bytes | Kafka 中 log 日志是分成一块块存储的,此配置是 指 log 日志划分 成块的大小,默认值 1G。 |
log.index.interval.bytes | 默认 4kb,kafka 里面每当写入了 4kb 大小的日志 (.log),然后就往 index 文件里面记录一个索引。 |
log.retention.hours | Kafka 中数据保存的时间,默认 7 天。 |
log.retention.minutes | Kafka 中数据保存的时间,分钟级别,默认关闭。 |
log.retention.ms | Kafka 中数据保存的时间,毫秒级别,默认关闭。 |
log.retention.check.interval.ms | 检查数据是否保存超时的间隔,默认是 5 分钟。 |
log.retention.bytes | 默认等于-1,表示无穷大。超过设置的所有日志总 大小,删除最早的 segment。 |
log.cleanup.policy | 默认是 delete,表示所有数据启用删除策略; 如果设置值为 compact,表示所有数据启用压缩策 略。 |
num.io.threads | 默认是 8。负责写磁盘的线程数。整个参数值要占 总核数的 50%。 |
num.replica.fetchers | 副本拉取线程数,这个参数占总核数的 50%的 1/3 |
num.network.threads | 默认是 3。数据传输线程数,这个参数占总核数的 50%的 2/3 。 |
log.flush.interval.messages | 强制页缓存刷写到磁盘的条数,默认是 long 的最 大值,9223372036854775807。一般不建议修改, 交给系统自己管理。 |
log.flush.interval.ms | 每隔多久,刷数据到磁盘,默认是 null。一般不建 议修改,交给系统自己管理。 |
节点服役和退役
服役(添加节点)
-
查看当前topic的分区规则 kafka-topics.sh --bootstrap-server node1:9092 --topic test_topic --describe
-
新增node3节点
-
创建一个负载均衡主题 vim topics-to-move.json
{ "topics": [ {"topic": "test_topic"} ], "version": 1 }
-
生成负载均衡计划
bin/kafka-reassign-partitions.sh --bootstrap-server node1:9092 --topics-to-move-json-file topics-to-move.json --broker-list "0,1" --generate # 之前的负载均衡计划 可以用于回滚 Current partition replica assignment {"version":1,"partitions":[{"topic":"test_topic","partition":0,"replicas":[0],"log_dirs":["any"]},{"topic":"test_topic","partition":1,"replicas":[0],"log_dirs":["any"]}]} # 重新生成分区的计划 Proposed partition reassignment configuration {"version":1,"partitions":[{"topic":"test_topic","partition":0,"replicas":[0],"log_dirs":["any"]},{"topic":"test_topic","partition":1,"replicas":[1],"log_dirs":["any"]}]}
-
复制重新生成分区计划 vim increase-replication-factor.json
{"version":1,"partitions":[{"topic":"test_topic","partition":0,"replicas":[0],"log_dirs":["any"]},{"topic":"test_topic","partition":1,"replicas":[1],"log_dirs":["any"]}]}
-
执行负载均衡计划
bash ../bin/kafka-reassign-partitions.sh --bootstrap-server node1:9092 --reassignment-json-file ./increase-replication-factor.json --execute # 之前分片计划 可以用于回滚 Current partition replica assignment {"version":1,"partitions":[{"topic":"test_topic","partition":0,"replicas":[0],"log_dirs":["any"]},{"topic":"test_topic","partition":1,"replicas":[0],"log_dirs":["any"]}]} Save this to use as the --reassignment-json-file option during rollback Successfully started partition reassignments for test_topic-0,test_topic-1
-
验证
bash ../bin/kafka-reassign-partitions.sh --bootstrap-server node1:9092 --reassignment-json-file ./increase-replication-factor.json --verify Status of partition reassignment: Reassignment of partition test_topic-0 is complete. Reassignment of partition test_topic-1 is complete. Clearing broker-level throttles on brokers 0,1 Clearing topic-level throttles on topic test_topic
-
此时再查看topic的描述,此时副本和leader已经改变
退役(移除节点)
- 查看当前topic的分区规则 kafka-topics.sh --bootstrap-server node1:9092 --topic test_topic --describe
-
此时如果需要对node2节点进行下线
-
编写需要执行负载均衡的主题json文件
{ "topics": [ {"topic": "test_topic"} ], "version": 1 }
-
生成一个负载均衡计划 bash …/bin/kafka-reassign-partitions.sh --bootstrap-server node1:9092 --topics-to-move-json-file ./topics-move-plan.json --broker-list “0” --generate
-
此时异常表示因为目前broker只有1台机器 但是replication-factor是2 无法做到副本为2此时修改partition0,1的副本为1
-
生成副本修改计划 /bin/kafka-reassign-partitions.sh --bootstrap-server node1:9092 --reassignment-json-file ./decrease-replication-factor.json --execute
{ "version": 1, "partitions": [ { "topic": "test_topic", "partition": 1, "replicas": [ 0 ] }, { "topic": "test_topic", "partition": 0, "replicas": [ 0 ] } ] }
-
再执行负载均衡计划 bash …/bin/kafka-reassign-partitions.sh --bootstrap-server node1:9092 --topics-to-move-json-file ./topics-move-plan.json --broker-list “0” --generate
-
此时再查看topic的状态 bash …/bin/kafka-topics.sh --bootstrap-server node1:9092 --topic test_topic --describe
- leader replicas 和ISR 都变成了0
kafka副本
- 副本作用可以提高数据可靠性
- 生产环境一般配置为2,可以保证数据可靠性,太多的副本会增加磁盘占用,且在数据副本同步的时候会占用网络资源降低效率
- Kafka中的副本分为leader和follower,producer只会把数据发往leader然后follower找leader进行数据同步
- kafka分区中所有的副本统称AR(Assigned Replicas)
- AR = ISR + OSR
- ISR: 表示leader保持同步的follower集合.如果follower长时间未向leader发送同步通信请求或者同步数据,该follower会被踢出ISR,时间阈值由replica.lag.time.max.ms控制默认30s.当leader发生故障时候会从ISR中重新选举新的leader
- OSR: 表示follower和leader副本同步时,延迟过高的副本
leader选举流程
kafka集群中有一个broker的controller会被选举成为controller leader,负责管理整个集群的broker的上下线,所有topic的分区副本分配和leader选举
故障迁移细节
LEO (Log End Offset): 每个副本的最后一个offset+1 即使最新的offset+1
HW (High Waterark): 所有副本中最小的LEO
follower故障
- follower发生故障之后会被临时踢出ISR
- 期间leader和其他follower继续接受数据
- 待follower恢复之后,follower会读取本地磁盘记录中的上次的HW,并将log文件高于HW的部分截取掉,从HW开始向leader进行同步
- 等到follower的LEO>=该partition的HW,即使follower追上了leader此时重新加入ISR
- 如上图如果broker2 宕机,宕机瞬间follower的HW和LEO 分别是5和7,此时leader和follower继续接受数据HW和LEO后移,此时broker2恢复发现自己的HW是5,先将HW之后的6,7清除,重新与leader通信进行HW和LEO的同步
leader故障
- leader故障之后,会从ISR中选出新的leader
- 为了保证多副本之前的数据一致性,其他follower会先自己log文件高于HW的部分截掉,然后重新从leader同步数据
- 保证了各个副本之间的数据一致性,但是不能保证数据不丢失和不重复
手动调整副本数
具体的生产环境可能因为每个服务器的配置和剩余资源不一样,如果全部根据kafka的默认规则进行负载均衡可能会造成资源的利用率不高,此时需要手动调整分区和副本的存储
-
创建副本存储计划 vim increase-replication-factor.json
{ "version":1, "partitions":[{"topic":"test_topic","partition":0,"replicas":[0,1]}, {"topic":"test_topic","partition":1,"replicas":[0,1]}, # 修改分区的replicas即可 {"topic":"test_topic","partition":2,"replicas":[1,0]}, {"topic":"test_topic","partition":3,"replicas":[1,0]}] }
-
执行修改副本存储计划
bash bin/kafka-reassign-partitions.sh --bootstrap-server node1:9092 --reassignment-json-file increase-replication-factor.json --execute
-
验证
bin/kafka-reassign-partitions.sh -- bootstrap-server node1:9092 --reassignment-json-file increase-replication-factor.json --verify
-
查看分区描述describe
bin/kafka-topics.sh --bootstrap-server node1:9092 --describe --topic test_topic
Leader Partition 负载均衡
默认情况下,kafka会自动将Leader Partition均匀的分布到各个机器上,保证每一台的机器的读写吞吐量均匀,但是如果某一些broker宕机,会导致leader partition过于集中在少部分broker机器上,会导致部分broker压力过大,其他broker重启之后都是follower,导致读写请求很低,造成集群的负载不均衡
kafka通过三个参数来控制leader partition的负载均衡
auto.leader.rebalance.enable 是否开启 默认开启
leader.imbalance.per.broker.percentage 允许不平衡比例 默认10%
leader.imbalance.check.interval.seconds 检查leader负载均衡时间间隔 默认300s
leader.imbalance.per.broker.percentage计算规则
如下实例为例
Topic: test_topic Partition: 0 Leader: 0 Replicas: [3,0,2,1] Isr: [3,0,2,1]
Topic: test_topic Partition: 1 Leader: 1 Replicas: [1,2,3,0] Isr: [1,2,3,0]
Topic: test_topic Partition: 2 Leader: 2 Replicas: [0,3,1,2] Isr: [0,3,1,2]
Topic: test_topic Partition: 3 Leader: 3 Replicas: [2,1,0,3] Isr: [2,1,0,3]
如果此时broker0节点宕机为例,此时AR中优先副本是3,但3不是leader节点,所以不平衡+1,AR副本总数是4,计算出不平衡节点为 1/4 > 10% 需要再平衡
在生产环境下一般不开启自动平衡,因为触发leader partition的自动平衡会损耗性能,或者将自动触发平衡的leader.imbalance.per.broker.percentage 参数阈值调大,一般采用手动调整的模式
文件存储
kafka的文件存储,topic是一个逻辑上的概念,而partition是物理上的概念.一个partition对应一个log文件,log文件存储了producer生产的数据,producer生产的数据会不断的 追加 到log文件的尾部,为了提高log文件定位效率,kafka采用了分片和索引的策略.每一个partition文件分割成多个segement,每一个segement包含了 .index .log .timeindex等文件位于同一个目录下,该目录命名规则为topic名称+分区序号如 test_topic-0
Log文件和Index文件详解
- 默认每写入4kb大小的日志log之后,就会往index记录一个索引(稀疏索引)
- index文件保存了offset是相对offset,确保了offset的值占用空间不会过大
- 根据offset定位到segment文件
- 找到小于等于目标offset的最大offset对应的索引
- 定位到log文件
- 向下遍历找到目标record
参数 | 描述 |
---|---|
log.segment.bytes | Kafka 中 log 日志是分成一块块存储的,此配置是指 log 日志划分 成块的大小,默认值 1G。 |
log.index.interval.bytes | 默认 4kb,kafka 里面每当写入了 4kb 大小的日志(.log),然后就 往 index 文件里面记录一个索引。 稀疏索引。 |
文件清理策略
kafka默认日志保存时间为7天日志过期之后kafka提供了两种清理策略delete和compact两种
参数 | 描述 |
---|---|
log.retention.hours | 最低优先级小时,默认 7 天 |
log.retention.minutes | 分钟 优先级高于log.retention.hours |
log.retention.ms | 最高优先级毫秒 |
log.retention.check.interval.ms | 负责设置检查周期,默认 5 分钟 |
log.cleanup.policy | 日志清理策略 默认delete |
log.retention.bytes | 基于大小:默认关闭。超过设置的所有日志总大小,,默认等于-1,表示无穷大 |
基于时间以中segement所有记录中最大时间戳为该文件的时间戳
例如segement2最新的记录没有达到过期时间,这次该segement不会被清理
compact日志压缩原理
相同key的数据只保留最后一个版本,压缩之后的数据会导致offset不连续,该策略适合一些特殊的场景,比如key是用户id,value是用户信息,用户的信息是会 不断更新的,compact策略只保留最新数据
kafka的高效读写原理
- kafka本身是分布式集群,采用分区技术提高了并行度
- 读数据采用了稀疏索引可以快速定位消费的数据
- 顺序写
- 页缓存 + 零拷贝