序
卡芙卡,角色?

一、消息队列-kafka
1.1、kafka简介
Kafka是消息队列(Message Queue)的一种,它最初由LinkedIn公司开发,是一个分布式、分区的、多副本的、多订阅者,基于发布/订阅模式的消息队列,可以高效地进行大数据处理和实时数据流分析。目前被许多大公司所采用,比如:Splunk、TrendMicro、Wix、Netflix、Airbnb、Twitter等。
1.2、kafka特点
- 解耦:允许你独立的改变或扩展你的系统组件,而不影响通信,只要它们遵守同样的接口约束。
- 可恢复性:即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理
- 缓冲:有助于解决生产消息和消费消息的处理速度不一致的情况
- 灵活性和峰值处理能力:不会因为突发的超负荷请求而完全崩溃,消息队列能够使关键组件顶住突发的访问压力
- 异步通信:允许用户把一个消息放入队列,但不必立即处理它。用户可以选择在某个方便的时候处理消息
1.3、kafka架构

先理解相关概念:
- Producer:消息生产者,向Kafka集群发送消息的对象
- Consumer:消息消费者,从Kafka集群中拉取消息进行消费
- Consumer Group(CG):,消费者组内每个消费者负责消费不同分区的数据,提高消费能力。一个分区只能由组内一个消费者消费,消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者
- Broker:Kafka集群中包含的服务器,一个Kafka集群可以有多个Broker,每个Broker可以容纳多个Topic
- Topic:消息主题,消息的分类,发布和订阅都是针对Topic的。一个Topic可以有多个生产者向其发送消息,也可以有多个消费者从中读取消息
- Partition:Topic的分区,一个Topic可以有多个分区,分区的作用是提高Topic的并发能力。每个分区内部是有序的,不同分区之间无序
- Replication::每一个分区都有多个副本。当主分区(Leader)故障的时候会选择一个备胎(Follower)上位,成为Leader。在kafka中默认副本的最大数量是10个,且副本的数量不能大于Broker的数量,follower和leader绝对是在不同的机器,同一机器对同一个分区也只可能存放一个副本(包括自己)。
- Leader: 每个分区多个副本的"主",生产者发送数据的对象,以及消费者消费数据的对象都是Leader
- Follower:每个分区多个副本中的"从",实时从Leader中同步数据,保持和Leader数据的同步。Leader发生故障的时候,某个Follower会成为新的Leader
- Offset:消费者消费的位置信息,监控数据消费到什么位置,当消费者挂掉再重新启动的时候,可以从上次的位置开始消费
- Zookeeper:Kafka集群依赖Zookeeper来保存集群元数据,实现负载均衡。同时,Kafka通过Zookeeper,将Broker、Topic、Partition信息存放在Zookeeper上,实现动态的集群扩展
通过图可以知道:
- Kafka 存储的消息来自任意多被称为 Producer 生产者的进程。数据从而可以被发布到不同的 topic 主题下的不同 Partition 分区。
- 在一个分区内,这些消息被索引并连同时间戳存储在一起。其它被称为 Consumer 消费者的进程可以从分区订阅消息。
- Kafka 运行在一个由一台或多台服务器组成的集群上,并且分区可以跨集群结点分布。
1.4、kafka工作流程

- Kafka 中消息是以 topic 进行分类的,生产者生产消息,消费者消费消息,面向的都是同一个 topic
- topic 是逻辑上的概念,而 partition 是物理上的概念,每个 partition 对应于一个 log 文件,该 log 文件中存储的就是 producer 生产的数据
- 生产者生产的数据会被不断追加到该 log 文件末端,且每条数据都会分配一个特定的 offset(偏移量),offset 为一个 long 型数字,它可以唯一标识一条数据
- 消费者组中的每个消费者,都会实时记录自己消费到了哪个 offset,以便出错恢复时,从上次的位置继续消费
日志默认在:/tmp/kafka-logs
1.5、kafka副本原理(Replication)

如图一个有 3 台 Broker 的 Kafka 集群上的副本分布情况。从图中可以看到,主题 1 分区 0 的 3 个副本分散在 3 台 Broker 上,其他主题分区的副本也都散落在不同的 Broker 上,从而实现数据冗余 。
数据冗余 :在一部分节点宕机的时候,系统仍能继续工作,提高可用性。
基于领导者的副本机制 (Leader-based Replication)---------类似于主从复制 机制

- kafka副本机制中的追随者副本是不对外提供服务的。
- 当领导者副本挂掉了,或领导者副本所在的 Broker 宕机时,Kafka 依托于 ZooKeeper 提供的监控功能能够实时感知到,并立即开启新一轮的领导者选举,从追随者副本中选一个作为新的领导者。老 Leader 副本重启回来后,只能作为追随者副本加入到集群中。
1.6、分区与主题的关系
- 一个分区只能属于一个主题
- 一个主题可以有多个分区
- 同一主题的不同分区内容不一样,每个分区有自己独立的offset
- 同一主题不同的分区能够被放置到不同节点的broker
- 分区规则设置得当可以使得同一主题的消息均匀落在不同的分区
1.7、生产者

生产者在写数据的时候,找的永远都是Leader, 不会直接将数据写入 follower.
1.8、分区策略
所谓分区策略就是决定生产者将消息发送到哪个分区的算法,kafka提供了以下几种分区策略:
- 轮询策略
- 随机策略
- 按消息键保存策略
- 默认分区
1.8.1、轮询策略
轮询策略即顺序分配。
比如一个主题下有 3 个分区,那么第一条消息被发送到分区 0,第二条被发送到分区 1,第三条被发送到分区 2,以此类推。当生产第 4 条消息时又会重新开始,即将其分配到分区 0

- 优点:非常优秀的负载均衡表现,能保证消息最大限度地被平均分配到所有分区上
1.8.2、随机策略
随机分配,是老版本使用的分区策略,新版本已改为轮询策略
1.8.3、按消息键保存策略
Kafka 允许为每条消息定义消息键,简称为 Key。一旦消息被定义了 Key,那么就可以保证同一个 Key 的所有消息都进入到同一个分区 。由于每个分区下的消息都是有序的,故也就可以保证同一个 Key 的所有消息都是有序的。
比如 key1 落在同一分区,key2 落在同一分区,key3 落在同一分区
- 优点:可以保证同一个 Key 的所有消息都进入到同一个分区,同一个分区下的消息是有序的
- 缺点:无法达到负载均衡的效果
1.8.4、默认分区
关键判断点:是否指明 partition 和 key
- 如果指明了 partition,则直接将消息存到指定的 partition
- 如果没有指明 partition,但是有 key,则对 key 进行 hash,然后对分区数取模,将消息存到对应的分区
- 如果既没有 partition,又没有 key,则用轮询策略,将消息存到轮询到的第一个分区
1.9 消费者
在传统的消息队列当中,消息一旦被消费,就会从队列中删除 。很显然,这种模型的伸缩性很差
通过 发布/订阅模型倒是可以让消息被多个Consumer消费,但伸缩性也不高,因为这要求每个订阅者都必须要订阅主题的所有分区 ;这种全量订阅的方式既不灵活,也会影响到消息的真实投递效果.
kafka提供了Consumer Group 消费者组机制,优雅地统一了点对点与发布/订阅两大模型。
消费组机制:
当 Consumer Group 订阅了多个主题后,组内的每个实例不要求一定要订阅主题的所有分区,它只会消费部分分区中的消息。Consumer Group 之间彼此独立,互不影响,它们能够订阅相同的一组主题而互不干涉。再加上 Broker 端的消息留存机制,Kafka 的 Consumer Group 完美地规避了上面提到的伸缩性差的问题。
简单点说:
- 如果所有实例(消费者)都属于同一个 Group,那么它实现的就是点对点消息队列模型
- 如果所有实例(消费者)分别属于不同的 Group,那么它实现的就是发布 / 订阅模型
二、Kafka分区分配策略
一个消费者可以订阅多个主题,可以消费多个分区,但是一个分区不支持多个消费者(同一个消费组)读取
可是,一个消费组中存在多个消费者,一个主题中又存在多个分区,那么 Kafka 是如何将分区分配给消费者的呢?
Kafka 提供了以下四种分配策略 (同上面的分区策略有区别):
- Range 策略
- RoundRobin 策略
- Sticky 策略
- CooperativeSticky 策略
2.1、Range 策略
原理:
按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。如果分区数不能被消费者数量整除,那么前几个消费者会被多分配一个分区。
假设 n = 分区数 / 消费者数量, m = 分区数 % 消费者数量========> 前m个消费者每个分配 n + 1 个分区,后面的(消费者数量 - m)个消费者每个分配 n 个分区。
举例:
假设消费组中存在两个消费者,C0,C1,都订阅了主题 t0 和 t1,并且每个主题都有4个分区,那么订阅的所有分区可以标识为:t0p0,t0p1,t0p2,t0p3,t1p0,t1p1,t1p2,t1p3。最终分配结果为:
C0:t0p0,t0p1,t1p0,t1p1
C1:t0p2,t0p3,t1p2,t1p3
再假设每个主题只有3个分区,那么订阅所有分区可以标识为:t0p0,t0p1,t0p2,t1p0,t1p1,t1p2。最终分配结果为:
C0:t0p0,t0p1,t1p0, t1p1
C1:t0p2, t1p2
可以明显看到,这种策略也不是总是保证分区尽可能均匀地分配给所有的消费者,如果分区数不能被消费者数量整除,那么前面的几个消费者会多分配一个分区。
2.2、RoundRobin 策略
原理:
将消费组内所有消费者及消费者订阅的所有主题的分区按照字典序排序,然后通过轮询算法逐个将分区依次分配给每个消费者。
如果同一个消费组内所有的消费者的订阅信息都是相同的,那么RoundRobin 策略能够实现分区均匀分配的效果。
举例:
假设消费组中存在两个消费者,C0,C1,都订阅了主题 t0 和 t1,并且每个主题都有3个分区,那么订阅所有分区可以标识为:t0p0,t0p1,t0p2,t1p0,t1p1,t1p2。最终分配结果为:
C0:t0p0,t0p2,t1p1
C1:t0p1,t1p0,t1p2
如果同一个消费组内的消费者订阅的信息是不相同的,那么在执行分区分配的时候就不是完全的轮询分配,有可能导致分区分配得不均匀。
如果某个消费者没有订阅消费组内得某个主题,那么在分配分区得时候,该消费者将不会分配到这个主题的任何分区。
举例:
假设消费组内有3个消费者C0,C1,C2,他们共订阅了3个主题t0,t1,t2,这3个主题分别有1,2,3个分区,消费者C0订阅主题t0,消费者C1订阅主题t0和t1,消费者C2订阅主题t0,t1和t2,那么订阅所有分区可以标识为:t0p0,t1p0,t1p1,t2p0,t2p1,t2p2。最终分配结果为:
C0:t0p0
C1: t1p0
C2: t1p1,t2p0,t2p1,t2p2
2.3、Sticky 策略
原理:
Sticky 策略是在 Kafka 0.11 版本引入的,它是在 RoundRobin 策略和 Range 策略的基础上改进而来的。旨在减少不必要的分区移动(重平衡) 的分配策略.
主要为了解决这两个策略存在的一些比较明显的问题:
当消费者组内成员发生变化(如消费者加入或离开)时,会触发再平衡,重新分配分区。RoundRobin 策略和 Range 策略 只追求分配结果的绝对均匀,而完全不考虑分配结果与上一次的连续性 。
这样就会导致:大量分区在消费者间"跳来跳去" 。例如,分区P1原本由消费者C1处理,再平衡后可能被分配给C2,意味着:
- 状态丢失:C1上关于P1的本地缓存将失效。
- 开销增加:C2需要重新建立与P1所在Broker的连接,并可能要从头加载状态,造成资源浪费和处理延迟
Sticky策略就是为了解决这个问题而生,它的两大核心目标是(按优先级排序):
-
分配尽可能均衡(这是所有分配策略的基础要求)。
-
分配结果尽可能与上一次分配保持一致
当两者冲突时,目标1优先于目标2。也就是说,它首先保证公平,然后在公平的前提下,极力维持稳定。
举例:
公司部门重组时员工分配:
-
RoundRobin 策略和 Range 策略:会将所有员工打散,完全按照新部门人数平均分配,完全不考虑员工原来在哪个部门
-
Sticky 策略:首先确保新部门人数尽可能均衡 ,然后在人数均衡的基础上,尽可能保持员工在原来部门的稳定。
2.4、CooperativeSticky 策略
原理:
Kafka 2.4.0 版本引入的,它是 Sticky 策略的扩展,它支持在不停止消费的情况下进行增量再平衡。
三、Kafka数据可靠性保证
3.1、副本机制
为保证 Producer 发送的数据,能可靠地发送到指定的 topic,topic 的每个 Partition 收到 Producer 发送的数据后,都需要向 Producer 发送 ACK(确认收到)。
如果 Producer 收到 ACK,就会进行下一轮的发送,否则重新发送数据。

3.2、ACK应答机制
Kafka 为用户提供了三种可靠性级别:
- 0:producer 不等待 broker 的 ack,这一操作提供了一个最低的延迟,broker 一接收到还没有写入磁盘就已经返回,当 broker 故障时有可能丢失数据;
- 1:producer 等待 broker 的 ack,partition 的 leader 落盘成功后返回 ack,如果在 follower 同步成功之前 leader 故障,那么将会丢失数据;
- -1(all):producer 等待 broker 的 ack,partition 的 leader 和 follower 全部落盘成功后才返回 ack。但是如果在 follower 同步完成后,broker 发送 ack 之前 leader 故障,那么将会丢失数据。
3.3、可靠性指标
- 分区副本:Kafka 中消息的可靠性是分区副本机制来保证的,每个分区有多个副本,其中一个是 leader,其他是 follower。leader 负责读写操作,follower 只负责同步 leader 的数据。一般情况下,副本的数量是 3 个,也就是每个分区有 1 个 leader 和 2 个 follower。
- ACKS:Kafka 中消息的可靠性是通过 ACK 来保证的,ACK 是指确认消息的机制,分为 0、1、-1(all)三个值。
四、拓展:
-
为什么分区可以水平扩展?
Kafka 的消息组织方式实际上是三级结构:主题 - 分区 - 消息。主题下的每条消息只会保存在某一个分区中,而不会在多个分区中被保存多份。

-
消费方式
Pull的方式从Broker中读取数据,而且Pull会根据Consumer的消费能力以适当的速率消费消息。如果 Kafka没有数据,消费者可能会陷入循环中,一直返回空数据;因为消费者从 Broker 主动拉取数据,需要维护一个长轮询,针对这一点, Kafka 的消费者在消费数据时会传入一个时长参数 timeout。
-
什么时候发送 ACK?
确保有 Follower 与 Leader 同步完成,Leader 再发送 ACK,这样才能保证 Leader 挂掉之后,能在 Follower 中选举出新的 Leader 而不丢数据