- 每个主题都有多个分区
- 每个分区都会有自己的多个副本
- 每个分区与自己的副本之间都会有一个leader
- 同一主题下的不同分区包含的消息不同(同一条消息不会出现在多个分区中)
- 由于消息是以可追加的log日志存储到分区中的,多个分区顺序写磁盘的总效率要比随机写内存还要高
- 消息被追加到分区日志文件时候都会分配一个特定的偏移量(也就是消息在区域中的位置offset,相当于坐(下)标)
- offset是消息在分区中的唯一标识,kafka通过offset来保证消息在分区中的顺序性
- 生产者与消费者只与leader副本交互, follower副本只负责同步leader消息
- 分区中的所有副本统称AR(AssignedReplicas)
- 与leader副本同步pull消息的副本组成ISR(In-Sync-Replicas)
- 与leader副本同步期间有一定范围许可的滞后的follower副本组成OSR(Out-Sync-Replicas)
- AR=ISR+OSR
- 当所有的follower副本都与leader副本保持同步就是AR=ISR,OSR集合为空
- leader副本负责维护和跟踪ISR集合中所有follower副本的滞后状态
- 当follower副本落后太多或失效时,leader副本会把它从ISR集合中剔除
- 如果OSR集合中有follower副本同步追上leader副本,leader副本会把它从OSR集合转移至ISR集合
- 默认下当leader副本发生故障后,只有ISR集合内的副本有资格被选举为新的leader
-
leader与HW(HighWatermark)和LEO(LogEndOffset)
-
LEO是分区里的最后一条消息的下标+1,也就是指针指的是下一个待写入的消息
-
HW是分区里副本消息同步数量最少的那个follower副本的最后一条消息的下标
-
若某一个分区的所有副本中,其中一个的follower只同步了部分消息,则HW会以follower副本里消息最少的那个下标为值
-
消费者只能消费HW以及之前的消息(下标是从0开始,所以实际取的最后一条是HW-1的消息)
-
所以kafka的复制机制不是真正意义上的同步复制,也不是单纯的异步复制。事实上,
同步复制要求所有能工作的follower副本都复制完,这条消息才会被确认为已经提交成功,
这种复制方式极大地影响了性能
2.而在异步复制方式下,follower副本异步地从Leader副本中复制数据,
数据只有被leader副本写入就被认为已经成功提交,这种情况下,如果follower副本
都还没有复制完而落后于leader副本,突然leader副本宕机,则会造成数据丢失。 -
kafka使用的这种ISR方式则有效的权衡了数据可靠性和性能之间的关系
1.acks=-1或acks=all是时,生产者需要等待ISR中所有的副本都成功写入消息后才能收到来自服务端的成功响应;这
种设置可达到最强的可靠性,要想获得更高可靠性需要配合min.insync.replicas参数的联动+
-
Kafka的每个消费者都有一个对应的消费组(逻辑概念),当消息发布到主题后,只会被投递给订阅它的每个消费组的中一个消费者。
-
kafka默认设置是每个消费组的消费者会平均分配订阅主题的分区数,每个消费组只能消费分配到的分区内的消息,也就是
说一个分区只能被一个同一个消费组内的一个消费者消费(分区是和消费者去直接对应,不和消费组关联)。
eg1:主题A有4个分区,现在有两个参与订阅A的消费组x和y;x内有4个消费者,y内有2个消费者
-x和y订阅A后,x的每个消费者只消费A的四个分区里面的其中一个分区,y内的2个消费者每个只负责消费A的两个分区。
而每个消费者只能消费所分分配到的分区内的消息。
eg2: 主题A有7个分区,现在只有一个消费组x订阅了A,而此时x内只有一个消费者
-此时x订阅后,只有一个消费者消费A的7个分区
-接着,x消费组又加入了一个消费者,则x组内共有两个消费者,现在两个消费者一个消费4个分区,一个消费3个分区。
-又接着一个消费者加入了x消费组,则现在3个消费者共同消费7个分区
...当加入的消费者数量大于分区数的时候,也就是当第八个消费者加入x消费组的时候是无法消费任何分区消息的,因为7个分区已经被其他7个消费者消费了。
(以上分配逻辑是默认的分区分配策略进行分析的)
-
当消费者个数大于分区个数就会有消费者分配不到任何分区而无法消费任何消息。
-
消息中间件,一般有两种消息投递模式: 点对点(p2p)模式和发布-订阅模式。
-
点对点模式是基于队列的,消息生产者发送消息到队列,消息消费者从队列中接收消息。发布-订阅模式在消息的一对多广播时采用。
kafka同时支持两种消息投递模式。 -
消息的消费一般有两种模式:push和pull。push模式是服务端主动将消息推送给消费者,而pull模式是消费者
主动向服务端发起请求来拉取消息。Kafka的消费是基于pull模式的。本质是通过poll()方法轮询拉取消息 -
在每次调用poll()方法时,它返回的是还没有被消费过的消息集,要做到这一点就需要offset这个消费位移/偏移量
的记录。而且这个位移必须做持久化保存不单单是保存在内存中,否则消费者重启之后就无法知晓之前的消费位移, -
在旧消费者客户端中,offset是存储在zk中的,而新客户端中是存储在kafka内部的主题_consumer_offsets中的。这里
将offset持久化的动作也叫"提交",消费者在消费完消息之后需要执行消费位移的提交。
Q:
- 分区内消息的状态分为已被消费者消费过的消息和从未被消费过的消息?
1.1 如果消息有状态的信息是如何保存的,状态的区分是在消息位移提交之前还是之后? - 如果已被消费过的消息 是不会被新的消费组消费么(不会,新的消费组已经无法分配到更多的分区所以是多余的)
- 消费组都是拉取这个分区内从未被消费过的所有消息么
- 分区一旦在第一次分给消费组之后便不会再次分配了么?