起因:在实际项目开发过程中,需要使用RabbitMQ来实现消息队列的功能,在运用过之后,也去学一学kafka,了解一下他们之间的差别,吃一吃架构方面的相关内容,提升自己。
1. kafka生产者分析
1.1. 生产者分区的原则
为什么要分区
提升了水平扩展能力
提供并发能力
分区的原则
指明partition的情况下,直降将指明的值作为partition的值(指定存放分区)
没有指明partition,但有key的情况下,会将key的hash值与topic的partition数量进行取余得到partition值
什么都没有指定,在第一发消息的时候,系统会随机生成一个整数来对topic的partition数量进行取余得到partition值,后面每次都对这个已经生成的随机数进行+1,这就得到了round-robin算法了
1.2. Kafka副本的复制方案
1.2.1. 副本的复制方式分析
Kafka内部发送响应的机制:为了保证producer的数据能够可靠的发送并保存到topic上,topic的每个partition收到发送的数据后,都需要向生产者发送ACK,如果生产者收到ACK,就会进行下一轮发送,如果没有收到就会重新发送
副本的复制是如何复制的?
Producer--->leader(follower1,follower2)
这个情况下应该如何向Producer发送ACK
方案一:确保半数以上的follower完成同步,就发送ACK,优点是延迟低,在选举新的leader的时候,如果容忍n台节点故障,就需要2n+1个副本
方案二:完成全部follower的同步,才发送ACK,缺点是延迟高,在选举新的leader的时候,如果容忍n台节点故障,只就需要n+1个副本
kafka使用方案二作为follower的同步方式
如果选择方案一:虽然网络延迟低,但数据一致性无法保障,而且需要2n+1个副本,副本过多就会导致数据冗余过大,造成很大浪费
虽然方案二延迟高,但对于kafka来说影响不大
1.2.2. 通过ISR优化副本同步
先看一下topic的详细信息
Topic: topicfirst PartitionCount: 5 ReplicationFactor: 1 Configs:
Topic: topicfirst Partition: 0 Leader: 11 Replicas: 11 Isr: 11
Topic: topicfirst Partition: 1 Leader: 11 Replicas: 11 Isr: 11
Topic: topicfirst Partition: 2 Leader: 11 Replicas: 11 Isr: 11
Topic: topicfirst Partition: 3 Leader: 11 Replicas: 11 Isr: 11
Topic: topicfirst Partition: 4 Leader: 11 Replicas: 11 Isr: 11
在kafka采用第二种方案进行副本复制后进行ACK响应,会等待所有follower完成同步,这个时候如果有一个follower因为某种原因无法访问,这个时候leader就要一直等着这个follower来完成同步才能发ACK给producer
Kafka的解决方案:Leader维护了一个动态的in-sync replica set(ISR)
-
副本同步机制
作用是和leader保持同步的follower集合
当ISR中的follower完成数据同步之后,leader就会给follower发送ack(数据是由folloer主动从leader那里拉取过来的)
-
ISR是一个动态同步集合:从ISR中移除follower的条件是当follower长时间未向leader拉取数据,该follower就会被剔除出ISR,时间阀值由:replica.lag.time.max.ms=10000 决定 单位ms
replica.lag.max.messages 这个是leader和follower的消息数的差,超过就剔除出ISR,这个参数在0.9版本已经移除
当leader发生故障了,就会从ISR中选举新leader
2. Kafka生产者的ACK机制(可靠性)
ACK(在rabbitmq里面,我们producer和broker的一个反馈是什么?callback,return)
对于kafka不太重要的数据是不是就不需要可靠性很高了
副本机制 主分片--副本分片
producer发送给broker-->partition(leader)-->replication(2)
这个时候,我们思考一个生产者的ACK机制,p roducer通过一个配置项目ACKS
acks = 0 : producer只要给到broker就返回ack,当broker接收到数据后,如果broker故障导致数据丢失
acks =1 : partition的leader落盘成功后才返回ACK,不关心follower,当我们的partition的leader挂掉后数据就无法同步到follower(leader挂了,要选举生成新的leader)
acks = -1 : 所有ISR中的分区都同步成功才会返回ACK给producer
kafka的producer在没有接收到ACK后会重试,这个重试是有次数的,这个次数是你配置的
3. Kafka分布式保存数据一致性问题
producer有一个重试机制,如果数据没有接收到ACK的情况下,重新再次发送
场景分析:如果有一个leader,两个follower,当leader宕机了
[图片上传失败...(image-655708-1602662879949)]
LEO(Log End Offset):每个副本最后一个offset
HW(High Watermark):所有副本中最小的那个LEO(7)
数据一致性的执行细节:
1、follower故障
follower发生故障就会被剔除出ISR,待follower恢复后,follower会读取本次磁盘上上次记录的HW(7),将log文件中高于HW部分截取掉,从HW开始向leader进行同步,待follower的LEO大于等于Partition副本的HW,当follower追上leader以后,就可以重入ISR
2、leader故障
leader故障之后,会从ISR中选一个follower成为leader,为保证多个副本间的数据一致,将所有的副本各自的高于HW的数据部分截取掉,从新的leader同步数据
注意:这个只能保证数据一致性,不能保证数据不丢失或者不重复
4. Kafka的Exactly Once实现
将producer的ack设置为-1,保证数据producer到partitons的数据不丢失,就是At Least Once,如果将ack设置为0,可以保证每条消息只会发送一次,即At Most Once
At Least Once可以保证数据不丢失,但不能保证数据不重复,At Most Once可以保证数据不重复,但不能保证数据不丢失
Exactly Once = At Least Once + 幂等性
At Least Once 可以通过Producer的ACKS设置为-1来解决,在kafka的v0.11(含)之后引入了一个新特性:producer端的幂等性,无论Producer发给broker多少次,只要数据重复,broker只会持久化一条给到topic
在Producer端通过参数 enable.idempotence 设置为true即可,相当于开起了producer端的幂等性:Producer在初始化的时候会被分配一个PID,发往同一个Partition的消息会附带Sequence Number。broker端会对
<PID,Partition,Sequence Number>做主键缓存,当有相同主键信息只会持久化一条了
但是:系统只要重启就会更新PID,在不同的Partition上会有不同的主键,所以Producer的幂等无法保证跨分区跨会话的Exactly Once
5. Kafka生产者的事务机制
kafka的数据可以有很多的partition
场景:当producer个p0,p1,p2写入数据,0-10,1-15,正要给2分区写数据broker挂了,如果acks=1,有主分区没有写入完成,producer会重试发送
在kafka的v0.11版本引入了transactionID,将transactionID和PID绑定并保存事务状态到一个内部的topic中,当服务重启后该事务状态还能获取
6. Kafka发送消息的流程
kafka的producer发送消息采用的是异步发送模式,一个main一个sender还有一个线程共享变量(RecordAccumulator)
[图片上传失败...(image-4ad6e3-1602662879948)]
batch.size : 数据积累到多大以后,sender才会发送
linger.ms : 如果一直没有达到batch.size,sender会等待linger.ms时间后就发送
不要以为每天把功能完成了就行了,这种思想是要不得的,互勉~!