赞
踩
Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。Kafka是一个分布式的基于发布/订阅模式的消息队列,相比于其他常见的消息队列,Kafka的特点是高吞吐量极高,主要应用于大数据实时处理领域。
Kafka是一个常用的消息队列,消息队列就是在消息的传输过程中保存消息的容器。消息队列有很多种用途,可以用来给程序解耦合,可以让同步的程序变成异步,在运维和后台中,消息队列还会被用来削峰和限流。
下面举了一个消息队列的使用场景,让同步的程序变成异步运行。一个原本以同步方式进行运行的程序,用户填写注册表并提交,写入数据库成功后,系统调用发送短信接口发送短信,短信发送后用户会得到响应成功的返回信息。这种同步运行的方式有一个问题,就是调用短信接口的过程可能会耗费很多时间,所以会让用户等很久才能得到回复,然而其实用户注册信息写入数据库之后的步骤都已经不需要用户的介入了。面对这种场景,通过消息队列将同步处理变为异步处理可以很好的解决问题。用户注册信息写入数据库之后,将发送短信的请求写入消息队列暂存,然后发送短信的接口从消息队列中拿消息发短信,用户可以直接得到响应而不用一直等待短信发送完成后才能得到注册成功的信息。
这种异步的思想在前端框架ajax中也有体现。
消息生产者生产消息发送到Queue中,然后消息消费者从Queue中取出并且消费消息。消息被消费以后,Queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。
消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费,而且消费者消费数据之后不会清除消息。Kafka使用的就是这种模式
下面是一个Kafka集群的架构图:
broker :
一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。
topic :
可以理解为一个队列,生产者和消费者面向的都是一个topic。
producer :
消息生产者,就是向kafka broker发消息的客户端。
consumer :
消息消费者,向kafka broker取消息的客户端。
consumer group (CG):
消费者组,由多个consumer组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
partition:
为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。
replica:
副本,为保证集群中的某个节点发生故障时,该节点上的partition数据不丢失,且kafka仍然能够继续工作,kafka提供了副本机制,一个topic的每个分区都有若干个副本,一个leader和若干个follower。
leader:
每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是leader。
follower:
每个分区多个副本中的“从”,实时从leader中同步数据,保持和leader数据的同步。leader发生故障时,某个follower会成为新的follower。
Kafka中消息是以topic进行分类的,生产者生产消息,消费者消费消息,都是面向topic的。
topic是逻辑上的概念。为了方便扩展,并提高吞吐量,Kafka将一个topic分为多个partition,partition是物理上的概念,每个partition对应于一个log文件,该log文件中存储的就是producer生产的数据。为提高可用性,Kafka还为每个partition增加若干副本,防止因为节点宕机而产生的消息丢失。
producer生产的数据会被不断追加到partition也就是log文件的末端,且每条数据都有自己的offset。消费者组中的每个消费者,都会实时记录自己消费到了哪个offset,以便出错恢复时,从上次的位置继续消费。
由于生产者生产的消息会不断追加到log文件末尾,为防止log文件过大导致数据定位效率低下,Kafka采取了分片和索引机制,将每个partition分为多个segment。每个segment对应两个文件,以.index为后缀名的文件和以.log为后缀名的文件。这些文件位于一个文件夹下,该文件夹的命名规则为:topic名称+分区序号。例如,first这个topic有三个分区,则其对应的文件夹为first-0、first-1、first-2。
index和log文件以当前segment的第一条消息的offset命名。index文件存储大量的索引信息,log文件存储大量的数据,索引文件中的元数据指向对应数据文件中message的物理偏移地址。下图为index文件和log文件的示意图:
Kafka的分区就是将一个topic分成多个Partition进行存储,这种方式让Kafka集群的扩展性得到提高,每个Partition可以通过调整以适应它所在的机器,因此整个集群就可以适应任意大小的数据了。除了可以提高扩展性,通过分区还可以提高Kafka集群的并发性能,因为分区前consumer只能以topic为单位进行读写,分区后consumer可以以Partition为单位进行读写,Partition只是topic的一部分,所以这样设计让Kafka的并发性能非常好。
Kafka会将producer发送的数据封装成一个ProducerRecord对象。封装过程中需要设置分区策略。Kafka的分区策略一共有三种:
Kafka中的数据可靠性,指的是producer发送给Kafka的数据,不会丢失,也不会重复。
为保证producer正常发送数据,不受Kafka集群中节点变动的影响,可靠的将数据发送到指定的topic,topic的每个partition收到producer发送的数据后,都需要向producer发送ack(acknowledgement的缩写,确认收到),如果producer收到ack,就会进行下一轮的发送,否则重新发送数据。
Kafka集群有很多的follower和一个leader节点,producer向Kafka集群发送数据后,各个节点会同步自己接收到的数据。Kafka在确保有follower与leader同步完成后,leader才会发送ack,这样才能保证如果发生leader宕机挂掉的情况,能迅速在follower中选举出新的leader。至于在leader发送ack时有多少follower完成同步,是有两种方案的,这两种方案和选举机制有关。方案一:有半数以上的follower同步完成,即可发送ack。方案二:全部的follower同步完成,才可以发送ack。
方案一的优点是,延迟低,因为不用等所有follower同步完成,但是缺点也非常明显,选举新的leader时,容忍n台节点的故障需要2n+1个副本。也就是说,这种方式虽然发送ack的延迟很低,但是Kafka集群中的各个节点需要存储更多的数据副本。
方案二的优点是,选举新的leader时,容忍n台节点的故障需要n+1个副本,但是缺点是延迟高。
面对以上两种方案,Kafka选择了第二种方案,原因是,同样为了容忍n台节点的故障,第一种方案需要2n+1个副本,而第二种方案只需要n+1个副本,而Kafka的每个分区都有大量的数据,第一种方案会造成大量数据的冗余。虽然第二种方案的网络延迟会比较高,但网络延迟对Kafka的影响较小。
由于Kafka要等到全部的follower同步完成,才可以发送ack,那么很有可能会出现以下情景:leader收到数据,所有follower都开始同步数据,但有一个follower,因为某种故障,迟迟不能与leader进行同步,那leader就要一直等下去,直到它完成同步,才能发送ack。这种情况会让Kafka的效率大大降低,为了应对这种场景,Kafka设计了ISR。
Kafka的Leader维护了一个动态的ISR,全称是in-sync replica set ,意为和leader保持同步的follower集合。当ISR中的follower完成数据的同步之后,leader就会给follower发送ack。如果follower长时间未向leader同步数据,则该follower将被踢出ISR。如果是Leader发生故障,Kafka就会通过Zookeeper从ISR中选举新的leader。
对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,没必要等ISR中的follower全部接收成功。所以Kafka为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,选择不同的配置。acks有三种可用的参数配置,分别是0、1、-1:
acks=0:
producer不等待broker的ack,这一操作提供了一个最低的延迟,broker一接收到还没有写入磁盘就已经返回,当broker故障时有可能丢失数据。
acks=1:
producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将会丢失数据。
acks= -1:
producer等待broker的ack,partition的leader和follower全部落盘成功后才返回ack。但是如果在follower同步完成后,broker发送ack之前,leader发生故障,那么会造成数据重复。
默认情况下,acks会被设置为-1,避免数据丢失的情况。
Kafka集群中,如果producer在发送数据时,如果follower和leader发生故障,那么集群就需要最一些故障处理,避免发生数据读取的混乱。
Kafka故障处理过程有两个概念:LEO和HW,他们是两个位置标识符。
LEO:每个副本的最后一个offset。
HW:所有副本中最小的LEO,HW之前的数据对Consumer可见,HW之后的数据对Consumer不可见。
follower发生故障后会被临时踢出ISR,待该follower恢复后,follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截取掉,从HW开始向leader进行同步。等该follower的LEO大于等于该Partition的HW,即follower追上leader之后,就可以重新加入ISR了。
leader发生故障之后,会从ISR中选出一个新的leader,之后,为保证多个副本之间的数据一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader同步数据。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。