一、kafka概述
1、定义
kafka是一个分布式的基于发布订阅模式的消息队列,主要用于大数据实时处理领域
2、消息队列
【1】应用场景
用于异步、削峰、解耦
【2】两种模式
(1)点对点模式
一对一,消费者主动拉取数据。消息生产者将消息发送到queue中,然后消费者从queue中取出并消费消息,消息被消费者消费以后,queue中不在存储消息,所以消息消费者不可能重复消费已经被消费过的消息。queue支持存在多个消费者,即一个消费者组可以有多个消费者,但是消息只能被一个消费者消费。
(2)发布订阅模式
一对多,消费者消费数据之后不会清除消息数据
消息生成者发布者将消息发布到topic中,同时有多个消息消费者订阅消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。
二、kafka基础架构
1、架构介绍
kafka架构 由producer、consumer group、broker组成,producer是消息的生产者,consumer是消息的消费者,broker是代理服务器,消息都存放在broker上。producer、consumer要想向kafka发送和消费数据需要先申请一个主题,都是面向主题进行操作,申请创建topic的时候需要指定分区的个数,副本的个数,kafka集群会采用轮询或者range分区的方式将分区分散到不同的broker上。
为了方便扩展,提高吞吐量,一个topic分为多个partition
配合分区的设计,提出消费者组的概念,组内每个消费者并行消费
为提高可用性,每个partition增加若干副本,类似于namenode Ha结构
2、角色介绍
producer : 消息的生产者,向kafka broker发送消息
consumer :消息的消费者,从broker拉去消息进行消费
consumergroup :消费者组,一个消费者组里可以有多个消费者,组内每个消费者可以负责消费topic不同分区的数据,可以并行消费,一个消费者组逻辑上是一个订阅者。
broker :kafka代理,即kafka代理服务器,一个集群由多个broker组成,一个broker可以容纳多个topic。
topic : 主题,可以理解成队列,但是和点对点队列不同的是,不同的消费者组都可以从topic拉去相同的消息。
由此引出推模型和拉模型的区别:
推模型 push :指定消息推送给谁,如果要给多个对象推送的话,需要推送多份。
拉模型 pull :消息发布出去,放到某个地方,感兴趣的自己来拉。只需要推一份数据。
partition : 分区,为了实现扩展性,一个非常大的topic可以分布到多个broker上,一个topic分为多个partition提高消息的发送和消费的速度,可以将一个partition看成一个有序的队列。
replica : 副本,为了保证集群中某个节点发生故障时,该节点上的partition数据不丢失,且kafka仍然能继续工作,kafka提供了副本机制,一个topic的每个分区都有若干个副本,一个leader和若干个follower。
leader : 每个分区多个副本的master,生产者、消费者消息发送和消费的对象都是和leader进行通信。
follower : 每个分区多个副本中的slave,实时从leader中同步数据,保持和leader数据的同步。leader发生故障时,某个follower会成为新的leader。
三、kafka工作流程和文件存储机制
1、工作流程
2、存储机制
kafka中的消息是以topic进行分类的,消息的生产者生产消息,消息的消费者消费消息都是面向topic的。
topic是逻辑上的概念,而partition是物理上的概念,一个partition对应一个log文件,producer每次生产的消息都会追加到该文件的末尾。
由于每次生产者生产消息都会追加到log文件中,为了防止日志文件过大影响消息的查找定位效率,kafka引入了分片和索引的机制,将一个partition又分为若干个segment,每个segment包含一个存储数据的log文件和查找的index索引文件,这些文件位于一个文件夹下,文件夹的命名规则为topic名称+分区序号。
log文件和index文件以该segment的第一条消息的offset命名。下图展示了如何根据offset来查找对应的消息。
根据offset找到index文件,在index文件中找到该offset消息在消息文件中的结束位置,根据上一条消息的结束位置和本条消息的结束位置就能得到该消息。
四、kafka生产者
1、分区选择策略
【1】分区的原因
a)方便在集群中扩张,每个partition可以通过调整以适应它所在的机器,而一个topic又可以由多个partition组成,因此整个集群就可以适应任意大小的数据了。
b)可以提高并发,以partition为单位进行读写
【2】分区的原则
我们需要将producer将消息包装成一个producerRecord对象发送出去
a)指明分区的情况话直接发送给该分区
b)没指明partition,对象存在key的时候,将key的hashcode对partition数进行取模得到partition值
c)上面都没指定的时候,第一次调用时随机生成一个整数,后面每次在这个整数上自增,将这个值与topic的partition数取模,得到partition位置,即round-robin算法轮询算法。
2、数据可靠性保证
为了保证producer发送的数据能可靠的发送到指定的topic,topic的每个partition收到producer发送的数据后都需要向producer发送确认消息,即acknowledgement确认收到消息,如果producer收到ack就会进行下一轮的发送,否则重新发送数据。
【1】数据可靠性问题描述
为保证 producer 发送的数据,能可靠的发送到指定的 topic,topic 的每个 partition 收到 producer 发送的数据后,都需要向 producer 发送 ack(acknowledgement 确认收到),如果 producer 收到 ack,就会进行下一轮的发送,否则重新发送数据。
【2】数据可靠性保证的方式
那么什么partition所在的broker什么时候发送ack给producer?有什么选择方案?
leader选举为啥会成为缺点?
因为需要半数以上的follower同意后才能选出leader,之所以需要半数以上是因为为了避免脑裂的问题SB问题。
kafka选取哪种方案?
kafka选择所有follower同步成功后再返回ack的方案,原因如下:
a–>同样为了容忍n台节点故障,第一种方案需要2n+1个副本,而第二种方案只需要n+1个副本,而kafka每个分区都有大量的数据,第一种方案会造成大量的数据冗余。
b–>第二种方案的网络延迟虽然比较高,但都是一个局域网中,网络延时的影响也比较小。
【3】ISR
kafka怎么解决高延迟的问题?ISR
如果一台follower因为某种原因迟迟不能同步数据,那么leader就需要一直等它而不能发送ack,为了解决这个问题,kafka采用了一个ISR方案。
leader维护了一个动态的in-sync-replica (与leader保持同步的副本集合,该集合中的副本和leader数据是一致的),当isr中的follower和leader数据同步完成之后,leader就会向follower发送ack,如果isr中的follower长时间未向leader发送同步完成消息,leader会将其从isr中剔除,等待的时长由replica.time.max.ms参数设定。leader故障之后,就会从isr中选举出新的leader。
【4】acks参数配置
0:producer不等待broker的ack,这种方式提供了一个最低的延迟,broker一接收到还没写入磁盘就已经返回,当broker故障时可能存在数据丢失。
1:producer等待broker的ack,partition的leader落盘成功后返回ack,如果follower同步成功之前leader故障,存在数据丢失。
-1: producer等待leader、follower全部落盘成功后返回ack,这种情况不存在数据丢失,但是,如果落盘成功后,leader故障还未来得及向producer发送ack,producer认为失败了会重新发送消息。就会造成数据重复。
要实现数据不重复,保证exactly-once,kafka需要进行重复判断(幂等),如果已经有该条offset数据就不重新写入了。
【5】故障处理
(1)follower故障
follower故障后会被leader踢出ISR,follower故障恢复时,需要恢复到和leader数据保持一致,follower会读取本地磁盘上次记录的HW并截取掉高于HW的offset消息,高于HW的消息重新冲leader同步获取。等follower的LEO大于等于该partition的HW,即follower追上leader后,重新加入到ISR中。
leader HW 和 LEO之间的数据对consumer是不可见的,因为follower数据还没有同步完成。
因此如果一个被剔除出ISR的follower需要恢复,重新加入到ISR中,它需要从它记录的上次HW开始同步leader数据,如果它上次记录的HW和LEO之间还有数据,这部分数据不是有效要丢掉,重新从leader同步,知道它的LEO追上现在leader的HW才能加入到ISR集合中
(2)leader故障
leader故障后,从ISR中重新选出一个新的leader,之后,为保证多个副本之间的数据一致性,其余的follower会现将各自的log文件高于HW的部分截掉,然后从新的leader同步数据
注意:者只能保证副本之间数据的一致性,并不能保证数据不丢失或者数据不重复。
【6】exactly one语义
对于某些比较重要的消息,我们需要保证exactly one,即每条消息被发送且只被发送一次。
在kafka 0.11版本之后,kafka引入了幂等性机制,配合acks=-1的at least once语义,实现了从producer到broker的exactly once语义。
即 at least once + idempotent = exactly once
使用时,只需要将enable.idempotent属性设置为true即可。kafka会自动将acks属性设为-1.
3、消息的发送过程
kafka的producer发送消息采用的是异步的方式。在消息发送的过程中,涉及到两个线程:main线程和sender线程,以及一个线程共享变量-recordAccumulator。main线程将消息发送给recordAccumulator,sender线程不断从recordAccumulator中拉去消息发送到kafka broker中。
相关参数配置:
batch.size : 只要数据累计到batch.size 之后,sender才会将这批数据发送出去。
linger.ms :如果数据迟迟未达到batch.size, sender等待该事件后会发送数据。
4、同步发送
同步发送指,一条消息发送后会阻塞当前线程,直至返回ack,send方法返回的是一个future对象,因此可以根据future对象的特点,实现同步发送的效果,通过future对象的get方法阻塞主线程。
五、kafka消费者
1、消费方式
consumer采用pull模式从broker中读取数据。
push模式很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。
pull 模式:消费者自己从broker中拉取消息。
push模式:由broker主动将消息推送给消费者。这种模式的目标是尽可能以最快的速度传递消息,但是这样很容易造成consumer来不及处理消息,典型的表现就是拒绝服务以及网络堵塞,而pull模式则可以根据consumer的消费能力以适当的速率消费消息。
pull模式不足之处是,如果kafka没有数据,消费者可能会陷入循环中,一直返回空数据,针对这一点,kafka的消费在消费数据时会传入一个时长参数timeout,如果当前没有数据可供消费,consumer会等待一段时间之后再返回,这段时长即timeout。
2、分区分配策略
一个consumer group中有多个consumer,一个topic有多个partition,所以必然会涉及到partition的分配问题,决定哪个partition由哪个consumer消费的问题。
kafka有两种分配策略,round-robin 和range
【1】round-robin
【2】range
3、offset的维护
由于consumer在消费过程中可能会出现断电宕机等故障,consumer恢复后,需要从故障前的位置继续消费,所以consumer需要实时记录自己消费到哪个offset,以便故障恢复后继续消费。
kafka 0.9版本前,consumer默认将offset保存在zookeeper中,从0.9版本后,consumer默认将offset保存在一个kafka的内置topic中,该topic为__consumer_offsets.
因此consumer在消费消息后需要向kafka提交offset,kafka配置了默认提交,也可以通过手动的方式进行提交。
六、kafka高效读写数据
1、顺序写磁盘
kafka的producer生产数据,需要写到log文件中,写的过程是一直追加到文件的末端,为顺序写,官网有数据表明,同样的磁盘,顺序写能达到600M/s,二随机写只要100K/s。这与磁盘的机械结构有关,顺序写之所以块是因为省去了大量磁盘寻址的时间。
2、零拷贝技术
七、zookeeper在kafka中的作用
kafka集群中有一个broker会被选举为controller,负责管理集群broker的上下线,所有topic的分区副本分配和leader选举等工作。
Controller的管理工作都是依赖于zookeeper的。
下图展示了topic的partition leader选举过程