Kafka
一、 消息队列概述
1. 消息队列
目前企业中比较常见的消息队列产品主要有Kafka、ActiveMQ、RabbitMQ、RocketMQ等。消息队列的在生产过程中的作用包括:缓冲/消峰、解耦、异步通信。
- 缓冲/消峰:控制数据流经过系统的速度,能够有效解决生产消息和消费消息速度不一致的情况。
- 解耦:确保生产者和消费者具有同样的接口约束的前提下,允许生产者和消费者扩展、修改两边的处理过程。不同数据源的数据可以发送到任意的存储系统。[外链图片转存中…(img-L1lNFPYq-1664344404726)]
- 异步通信:将消息存放到队列中,不需要立即对他进行处理,可以等到需要的时候再处理。
同步通信和异步通信的区别
同步通信:就像快递员给发送快递,要求接收方必须当面签收。只有双方同时在场,并且完成签收,才会结束本次任务;如果任何一方没有在场,就会造成另一方变成阻塞状态。
异步通信:就像快递员将快递放到快递驿站,放到驿站之后快递员就会继续执行下次任务,接收方会自行选择时间到驿站取快递,取完快递后才算完成本次的任务。
2. 消息队列的模式
- 点对点模式:消费者主动从队列中拉取数据,传输完成后清楚队列中的消息。
- 发布/订阅模式:
- 队列中存在多个主题(topic)的消息,消费者消费之后,不会清理队列中的消息。
- 存在多个消费者,并且消费者之间都是相互独立的,都可以从队列中消费消息。
- 队列中的消息清理工作,由消息队列本身决定。
二、 Kafka
1. Kafka定义
Kafka是分布式的、基于发布/订阅模式的消息队列,是一个开源的分布式事件流平台。
2. Kafka基础架构
Kafka由:Kafka Producer(生产者)、Topic、Kafka Consumer(消费者)组成。生产者负责将数据推送到Topic中存储,消费者将消息消费到对应的系统中。[外链图片转存中…(img-6d6fO9tF-1664344404727)]
1. Topic
Kafka是一个村粗多个topic的框架。一个Topic中存储着多个Partition,每个Partition中存储着不同的主题消息。不同的Partition可以存储在Kafka集群的不同节点。
2. Kafka Consumer
- Kafka中的消费者是以消费者组的形式存在,消费者组之间是相互独立的,但是同组中的消费者不是相互独立的。
- 同一个消费者组内的消费者共同消费一个Topic的消息,并且一个消费者消费过的Partition,组内其他消费者不能再次消费。
- 消费者组中的消费者数量与分区数无关,但是最好与分区数对应。
- 虽然一个分区只能被一个消费者消费,但是同一个消费者可以消费多个分区的消息。
3. Kafka Producer
Kafka的生产者在推送消息时产生了两个线程:main线程和sender线程。这两个线程是相互独立的,互不影响。
1. main线程
main线程中创建了一个双端队列RecordAccumulator。main线程将消息发送到队列,会经过拦截器、序列化器、分区器对消息进行处理,按批次推送到队列中。之后由sender线程将消息推送到Kafka Broker。
RecordAccumulator队列的默认大小是32MB
。每一个批次(batch size)的大小默认是16KB
。
2. sender线程
- sender将消息临时存放到缓存空间(NetworkClient),每个分区的数据默认有5个缓存请求。
- sender线程负责将队列中的数据按批次推送到Broker。
- 如果队列中的消息累积到batch size大小,就直接发送。
- 如果迟迟没有满足,则达到linger.ms(时间延迟)后将数据发送到Broker。
- 消息发送成功后,队列中的对应消息会被清理,如果发送失败则触发重试机制。
- 重试机制的默认值是int的最大值2147483647。默认重试间隔是100ms。
4. 架构的特点
- 一个topic中存在多个partition,便于进行扩展、提高数据的吞吐量。
- 消费者以消费者组的形式存在,组内的消费者并行消费,消费者组之间相互独立。
- 每个partition存在多个消息副本,副本存放在不同的kafka节点(broker),提高了数据的可用性。
副本机制:副本分为一个leader和多个follower,follower只负责数据的备份工作,leader负责数据的写入和读出
。
5. 分区的优点
- 合理利用存储资源:将数据按照分区存放在不同的Broker,降低了单台节点的传输压力,实现负载均衡。
- 提高了数据传输的并行度。
三、 数据可靠性
1. ack应答等级
生产者传输的消息分为三个等级:0、1、-1(all)。
- ack=0:生产者不等待broker落盘数据的应答。
- ack=1:生产者只等待leader落盘的数据应答。
- ack=-1:生产者等待所有leader和follower落盘的数据应答。
2. 数据可靠性分析
如果分区中的数据副本数设置为1,或者isr队列中应答的最小副本数为1,这跟ack等级设置为1的效果相同,存在丢数据的风险。
3. 幂等性
无论生产者向broker发送多少次重复的数据,broker只会持久化的保存一条。
数据写入Kafka时,会有全局主键(Pid,Partition,SeqNumber)。具有相同逐渐的消息提交时只会持久化的保存一条。
每次重启Kafka集群,Pid都会重新分配,Partition是分区编号,SeqNumber的值单调自增。
由此可见,
幂等性只能保证单分区内的单会话的数据不重复
。
4. 数据去重
1.至少一次(at least once)
ack=1,分区副本数设置大于等于2,isr队列应答最小副本数大于等于2
。- 至少一次能够保证数据不丢失,但是不能保证数据不重复。
2. 至多一次(at most once)
ack=0
。- 至多一次可以保证数据不重复,但是不能保证数据不丢失。
3. 精确一次(exactly once)
幂等性 + 至少一次
。- 精确一次可以保证数据不丢失也不重复,需要配合幂等性和事务使用。
- 跟钱相关的业务需要使用精确一次。
- 开启Kafka事务的前提是提前开启幂等性。
四、 Kafka Broker工作机制
Kafka集群启动后,集群中的Broker节点会经过选举机制,选出Leader和Follower,Leader负责数据的写入和读取,Follower只负责备份数据。
- AR:Kafka集群中所有的副本数。
- ISR:Kafka集群中与Leader同步信息的follower。
- OSR:已经挂掉的follower。
所以,集群中的 AR = ISR + OSR
。
1. Controller选举规则
Kafka启动之后,向集群中的Broker会向Zookeeper中注册节点信息,哪一个节点抢先注册成功,该节点的Controller就会辅助选举。第一次启动后,抢先注册成功的Broker就是Leader。
如果由broker挂了,Controller会先检查挂掉的节点中有没有Leader,如果没有就不会重新选举,如果有再进行选举。
选举规则
:按照Broker在AR中的排名顺序,仍然存活在ISR中为前提,哪个节点的排名靠前,谁就是新的Leader,如果再次出现节点挂掉,按照 isr 中存活的,AR 中排序顺序选举。
2. 故障处理机制
数据写入broker时,有两个参数标记每个节点上的数据传输进度。
- HW(High Watermark):所有副本中,最小的LEO的位置。
- LEO(Log End Offset):每一个副本中最后一个offset,即副本最后一个数据的位置。
1. Follower故障
- 一个follower挂掉之后,会被临时踢出ISR队列。其他正常工作的Leader和Follower继续传输数据。
- 挂掉的Follower恢复之后,会读取本地的挂掉之前记录的HW,并将log文件中高于HW的数据全部截掉,重新从本地的HW位置开始,从Leader备份数据。
- 当Follower的LEO高于该分区的HW后,重新加入ISR队列。
2. Leader故障
- Leader挂掉之后,会被临时踢出ISR队列,通过Controller选举出新的Leader。
- 数据会从新Leader的LEO处还是接收数据,其余的follower会将高于新Leader的LEO的数据截掉,重新弄从Leader处同步数据。
注意
:这种副本机制,只能保证数据的一致性,不能保证数据不丢失、不重复。数据不丢失、不重复跟ACK的等级设置有关。
五、 文件存储机制
1. 文件存储格式
数据由生产者推送到Topic中存储,由Leader和Follower进行接收和备份。文件以Log文件的形式存储,并且将每个partition的数据又分为多个Segment,每个Segment存储1GB的数据。Segment中的log文件存储真实数据,生产者产生的数据被不断地追加到log文件末端;index文件存储索引。这种机制叫做分片和索引机制。
Segment= .log文件 + .index文件 + timeIndex文件 + 其他文件
2. 如何定位目标文件的位置
-
同一个Segment中的index文件和log文件的文件名时一一对应的。
-
index文件中记录两个值:
offset(相对数据偏移量)、position(偏移量所处的位置)
。 index中存储的偏移量是相对偏移量,只是针对当前Segment中。绝对便宜量 = 相对偏移量 + index文件名中的数字索引。
index存储的是稀疏索引,大约每向log文件中写入4KB的数据,才向index中写入一条。
index文件和log文件中的查找算法都是二分法。在index中找到目标数据的范围之后,会到log文件中查找目标索引的起点的position位置,再通过log文件中的baseoffset和lastoffset判断出目标数据的位置。
为什么index中存储的是相对的offset?
因为存储相对offset,每个文件中的数据量不会很大,节约磁盘空间。如果存储绝对offset,假设index中记录了上亿条数据,那么offset的值会累加到很大,不便于存储和快速检索。
4. 文件清理策略
- kafka默认保留最近7天的日志文件,超过7天的就会被删除。
- 检查日志是否过期的周期,默认是5分钟。
1. delete删除
直接删除数据可以设置为是基于时间删除还是基于文件大小删除。默认情况下,开启基于时间删除文件,关闭基于文件大小删除文件。
只有当一个Segment中的文件全部过期,才会将该Segment删除,只有一部分过期数据,不会立即删除。
2. compact压缩
数据存储时按照K-V键值对形式存储,将相同Key的值压缩,并只保留最后一条Key对应的数据,将数据量变少。
例子:k1-1;k2-1,k3-1,最后只保留k3-1
5. Kafka如何实现高效读写
- Kafka是分布式的集群,可以实现分布式读写数据;
- 读数据采用稀疏索引机制,可以快速定位要消费的数据;
- Kafka写入数据是顺序写磁盘;
- 零拷贝技术:Kafka的数据加工处理操作,全部由Kafka生产者执行,broker不需要关心如何存储数据。
- 页缓存技术:类似于电脑的页缓存技术,Kafka提前将数据写入操作系统底层的PageCache中,查找数据时先从缓存中查找,缓存中没有再去集群中查找。
六、 Kafka的消费者组
1. Kafka消费者组的消费方式
-
Kafka消费者的消费方式是Pull(拉取)模式,根据消费者的性能,自行从队列中拉取数据。
-
topic中有多个分区时,会按照消费者和分区的数量进行平均分配
例子:topic有四个分区,组内两个消费者,则组内一号消费者消费前两个,二号消费后两个。
topic有5个分区,组内两个消费者,一号消费前三个,二号消费后两个。
-
消费者组进行消费时,_consumer_offsets中记录每一个消费者组的断点续传位置。如果某个消费者挂了,重启之后会继续从断点续传的位置开始读数据。这样不会丢失数据,但是如果消费者组的ID改变,数据也会发生改变。
2. 消费者组初始化流程
- 消费者组启动时会选出coordinator辅助组的初始化和分区分配。
- coordinator = groupid的hashcode值 % _consumer_offsets的分区数量。
- coordinator的值对应是哪个_consumer_offset的分区号,该分区在哪个broker上,就选择哪个节点作为这个消费者组的老大。该消费者组下的所有消费者提交offset时,都向该分区提交。
1. 消费者组工作流程
- 每个消费者都向coordinator发送JoinGroup请求。之后选出一个消费者组的Leader。
- coordinator将要消费的topic的情况发送给leader消费者,Leader消费者定制消费方案,并将方案返回给coordinator。
- coordinator得到方案后再将消费方案发送给各个组内消费者。
- 每个消费者跟coordinator默认3秒保持一次心跳,如果心跳时长超过45秒,或者消费数据时间超过5分钟,会触发消费再平衡。
2. 消费者工作原理
消费者组消费数据时,每个消费者都会创建自己的ConsumerNetworkClient客户端。消费者向客户端发送send请求,客户端又向Broker Leader发送send请求拉取数据。抓取数据后,数据会先存放到客户端内的消息队列中,消费者从队列中拉取数据进行消费。
- 消费者拉取数据时按批次进行拉取。
- 每批次拉取最小1字节的数据。
- 每批次默认最多拉取50MB数据。
- 如果始终没有满足拉取数据的最小值,默认每过500MS拉去一次数据。
- 消费者从自己队列中,每次拉取数据默认是500条。
3. 分区分配策略
Range分配策略
将topic中的分区按照分区号进行排序,消费者也按照字母进行排序。通过分区数/消费者数的结果来决定每个消费者应该消费几个分区的数据。如果除不尽,那么余数是N,那么前N个消费者每个都会多消费1个分区的数据。
此种策略适合topic数量较少时使用,topic数量过多容易造成数据倾斜。
RoundRobin策略
针对集群中的所有topic和消费者,将所有的都罗列出来按照hashCode值进行排序。通过轮询算法为每个消费者分配消费数据的分区。
Sticky:粘性分区及再平衡
进行一次分区之前,会考虑到上一次的分配结果,会尽量少的调整分配资源。如果组内的消费者出现问题,会尽量保证原有的分配策略不改变。
真实场景中,不会单独的使用某一种分区分配策略,会组合进行使用。
3. 数据积压
怎样会造成数据积压问题?
消费者的消费能力不足,或者下游的数据处理不及时(拉取数据/处理时间 < 生产速度),都会造成数据积压问题。
怎样解决数据积压问题?
消费者能力不足时,可以考虑增加topic的分区数,并同时增加消费者组内的消费者数量。将topic分区数和消费者数设置相同。必须同时满足上述两种条件。
如果是下游处理不及时:提高下游每批次拉取数据的数量。
如何提高Kafka的数据吞吐量?
生产者角度:增大仓库的默认大小,增大batch.size(批数据大小)和linger.ms(默认等待时间),将数据进行压缩传输。
消费者角度:增加消费者组内的消费者数量,提高每批次拉取数据的数量。
Broker集群角度:增加topic的分区数,提高并行生产、消费的效率。
题?**
消费者能力不足时,可以考虑增加topic的分区数,并同时增加消费者组内的消费者数量。将topic分区数和消费者数设置相同。必须同时满足上述两种条件。
如果是下游处理不及时:提高下游每批次拉取数据的数量。
如何提高Kafka的数据吞吐量?
生产者角度:增大仓库的默认大小,增大batch.size(批数据大小)和linger.ms(默认等待时间),将数据进行压缩传输。
消费者角度:增加消费者组内的消费者数量,提高每批次拉取数据的数量。
Broker集群角度:增加topic的分区数,提高并行生产、消费的效率。