写在前面
本文为Kafka系列文章第三篇,全文可见:
昨天写了一篇分享GRPC框架的博客竟然被jianshu给封了,也不知道这么纯粹的技术文章是哪里违反规定了 。Anyway,我已经申诉了 ,如果还是不行的话就换个平台继续写吧。
今天来讲讲Kafka的一些知识 ,关于Kafka我们都知道是消息队列组件,但是在全网搜了半天也没有一篇文章系统地来讲讲Kafka的消息处理的完整流程,信息显得太零碎了,所以我就动手总结了一下Kafka中消息的发送、存储和消费的完整流程。其中的流程图全部都是手画的,麻烦转载的时候至少跟我说一声哈。
Producer - 消息写入
消息写入-重点逻辑
- 幂等性发送:每个消息都在producer内根据topic,partition编号,对应leader通过校验编号实现有序接收和消息幂等
- 发送端的ACK配置:默认不等待,对于重要消息可以配置1或者2等待Leader及ISR的ACK消息
- ISR (in-sync replica set):
- leader在ZK中 (/brokers/<topic>/<partition/state)维护了一个与自身同步的follower集合
- 如果follower超过阈值时间 (replica.lag.time.max.ms,默认10s)未向leader拉取数据则会被剔出ISR。
- 可配置ISR最低值限制-min.insync.replicas,小于该数据则当前Partition不可用(无法写入数据)
Kafka集群-消息存储
消息是全量存储的但是会定期淘汰:
- 最大储量log.retention.bytes
- 最大保留时间log.retention.hours (小时)
文件存储结构
- 每个partition的表现为一个文件夹
- partition文件夹内部按照offset分segment,比如10000-19999,20000-29999
每个segment分为.log文件 (存储消息内容)和.index文件 (存储每个offset在log文件中位置/偏移量)
查找消息的时间复杂度
基本为稳定不变 —— O(1)+O(log(segment数量))+O(log(segment内行数))
- 根据partition,拼出文件名找到对应的文件夹:O(1)
- 文件夹内使用二分法找到这一offset所在的segment:O (log (segment_num))
segment数量不会很大,取决于最大储量/segment_size,比如就是45G/5G=9个文件,所以近似为常量 - 在segment.index文件内部,同样通过二分法找到这一offset所在的行,即可找到它在.log文件中的偏移量:O (log (lines_num))
lines_num最大值可配置,所以是常量
Consumer-消息消费
消息消费-重点逻辑
Rebalance:
触发条件:Partition或ConsumerGroup内的Consumer数量发生变化
分配逻辑:range-先均分,除不尽的部分按照字典序从前到后分配,所以4个partition分给3个consumer为:2 1 1Offset维护逻辑:
ConsumerGroup中的每个Partition的offset维护在_consumer_offset这个topic中
该Topic的每个Partition维护固定的ConsumerGroup中的所有数据(一对多)
提交Offset时是作为Producer向该Topic发送消息High Watermark:为leader+ISR中的最大offset的最小值,用于确保故障转移时多副本之间的数据一致性
HW由Leader维护和更新,Consumer最大可拉取到HW,其后内容不可见
故障转移时,Leader+所有的Follower会舍弃HW之后的内容,所有消息从HW重新开始累加
结尾
为了避免jianshu说我违规,参考链接暂时就不贴了。