kafka最初的使命是用于存储数据,所以天生会丢消息,一般在重要的业务系统中,都不会使用kafka。
核心概念
- broker:kafka集群中的一个节点;
- broker leader:负责而选举partition leader;
- coordinator:负责维护当前broker中各个partition被消费的offset;
- topic:kafka中消息的逻辑分类,也可以理解为一个消息通道;
- partition:为了提高吞吐量,topic被分成了多个partition(分区),partition内的数据有序;
- segment:每个partition在存储的时候,为了提高读写效率,被分成了多个segment段;
- consumer:消息的消费者;
- producer:消息的生产者;
- consumer group:消费者的一种高可用机制,在消费者组内,一条消息会被互斥消费;
- deplicas of partition:为了消息的高可用,每个partition会存在多分副本;
- partition leader:partition的多副本中,会存在一个leader负责数据的读写,其他副本只负责数据备份;
- partition follower:用于做数据备份的partition;
- ISR:partition的follower列表;
- offset:partition中的消息是有序的,每条消息都有一个唯一的偏移量,即offset;
- rebalance:默认情况下,一个消费者会消费一个或多个partition,或不消费任何partition,但是,当partition的数量发生变化或者consumer的数量发生变化时,对于consumer,partition的所有权就会在consumer之间转移,这个过程就做rebalance;
存储原理
kafka中消息的最小逻辑单元topic,在数据存储的时候,一个topic会被拆分成多个partition,每个partition会分散到集群中的不同节点上,同时,每个partition还会存在多分副本,保证数据的可靠性。
在存储数据时,一个partition对应了磁盘上的一个文件夹,而这个paritition中的消息会被拆分成多个segment,每个segment包含了三个核心文件:
- segmentname.index 消息偏移量索引文件
- segmentname.timeindex 以时间为维度的索引文件
- segment.log 消息文件
索引文件中,是以分段的方式来建立segment中数据的索引,这样就可以尽量减少一次数据检索的范围。
在创建topic的时候,要确定partition的数量是broker数量的整数倍,这样可以避免数据整个集群中发生倾斜。
HW阶段机制
在消费数据时,consumer从partition消费数据,但是并不是partition leader收到消息后,consumer就能够立即消费,而是当所有partition follower都同步完成后,这条消息才对consumer 可见,但是,如果在partition follower同步数据的过程中,partition leader宕机了,此时就会触发broker controller的leader选举,这时候就会从那些与原partition leader数据不一致的partition follower(ISR列表)中选出一个新的leader,但是,过了一段时间后,原partition leader又活过来了,此时,它作为partition follower,为了保证数据的一致性,这个重新活过来的partition follower就会从根据HW阶段机制将自己原有的与现在partition leader不一致的数据截掉。
HW阶段机制包含两个概念:
HW:High Water,高水位,用来标记最后一条被所有follower同步的一条标线;
LEO:Log End Offset:partition leader的接收到的最后一条数据;
当producer向partition 写入数据时,可以通过参数来配置是否需要等到partition leader向follower同步数据,这也是中保证写入数据成功的机制,每写一条消息LEO的offset就会进行调整,但只有当所有follower都将数据同步后,才会调整HW。
消息写入流程
- producer从zookeeper中获取partition leader;
- 将消息发给partition leader;
- partition leader将数据写入本地segment,并通知isr列表中的follower;
- partition follower从leader pull 数据并写入本地segment,写入成功后向leader返回ack消息;
- leader收到所有follower的ack消息后,返回给producer ack消息,一次写入成功;
发送消息时,可以通过request.required.acks参数来调整发送消息的可靠性:
0:异步发送,表示不需要kafka返回ack消息;
1:只要partition leader收到消息后就立即返回ack;
-1:等待所有partition follower都同步完成后,partition leader再返回producer给ack消息;
发送消息的可靠性主键增高,但是性能会逐渐下降,使用时,需要根据不能的业务场景进行调节。
消息路由规则
通过代码向kafka发送消息的时候,消息被封装成Record,每个Record包含了一个Key和一个消息体,Key主要用来做消息路由,但是也可以不指定Key值,具体的路由规则如下:
- 若指定了partition,则直接将消息发送到指定的partition;
- 如果没有执行paritition但指定了key值,那么通过key的哈希值与topic分区数取模来确定发往哪个partition;
- 若partition和key都没有指定,则通过轮询的方式发送到其中一个partition;
消息消费流程
- 消费者订阅指定的topic
- Broker Controller给这个消费者分配partition,并将对应的offset发给consumer;
- consumer 开始消费消息;
- consumer消费完消息后,向对应的Coordinator反馈;
- Coordinator更新对应的offset;
重复消费
- 同一个consumer重复消费
由于Consumer消费能力比较低,导致消费超时,最终offset不会被提交,最终导致重复消费。
- 不同的consumer重复消费
consumer消费了消息,但还未来得及提交offset就宕机了,
这些已经被消费过的消息会被其他consumer重复消费;
解决办法:
- 增加ack消息超时时限;
- 设置enable.auto.commit=false,手动进行ack消息的提交;