1.Kafka简介
Kafka是一个分布式的基于发布订阅模式的消息队列,主要针对大规模数据处理场景。
1.1 消息队列(Message Queue)的好处:
a.解耦
生产者和消费者可以独立发布服务,不会导致线上异常和数据丢失;
b.可恢复性
MQ通常具有重试机制,而且分组消费,数据存储到磁盘(支持副本),使数据不会丢失;
c.缓冲
避免生产者和消费者处理速度不一致带来的问题,如数据丢失,调用异常和超时等;
d.削峰
使用MQ可以一定程度上解决突发高流量的情况,将短时间内的流量,扩大到更长时间的维度,避免系统超负荷运行,甚至系统奔溃;
e. 异步通信
消费者可以不立即处理MQ中的消息,让MQ作为一种临时的数据存储方式(Kafka默认保存数据7天),等在需要的时候在进行消费处理。
f. 数据分发
当业务M需要调用多方(如A,B,C)时,普通的调用需要逐个调用,当增加D或者减少C时,当前业务需要修改代码以及重新上线等;但是使用MQ之后,当前业务M只需要将消息发送到MQ中,不用关心到底是谁消费了消息,消费者的增加和删除不影响业务M。
1.2 消息队列(Message Queue)的缺点:
a.系统可用性减低
加入MQ之后,服务就会依赖MQ,MQ宕机,那么服务也就不可用了。
b.系统复杂性增加:
同步改成异步之后,MQ消息的丢失,重复,顺序性等都需要考虑。
c.数据一致性问题:
在不同的分组消费MQ时,有的分组消费成功,有的消费失败,会导致各系统之间的数据不一致。
2.Kafka的架构:
2.1 消息队列的两种模式:
Producer A是发布订阅模式(一对多,数据消费后不会删除);
Producer B是点对点模式(一对一,一个消息只会有一个消费者消费,数据消费后会被删除);
2.2 架构说明:
1)Producer:
生产者向broker发送消息
2)Consumer:
消费Broker的消息,
3)Consumer Group:
一个group由多个consumer组成,消费者组内每个消费者负责消费不同的分区数据,一个分区只能由一个组内消费者消费,消费者组之间互不影响;
4)Broker:
一台Kafka机器就是一个Broker,一个Kafka集群由多个Broker组成,一个Broker可以容纳多个topic;
5)Topic:
主题队列,Producer和Consumer都是面向topic队列的;
6)Partition:
一个topic可以分成多个Partition,Partition分不到不同的Broker上,每个Partition是有序的队列;
7)Replica:
副本保证集群中一个节点发生故障时,该节点的partition不会丢失,保证Kafka仍然能够继续工作,一个leader partition对应多个follower;
8)leader:
主分区,生产者发送数据和消费者消费数据都是这对leader partition的;
9)follower:
从分区,从分区实时从leader主分区同步数据,当leader发生故障时,某一个从分区成为leader。
2.3 Kafka数据的保存:
Kafka中,topic是逻辑概念,partition是物理概念,每个partition对应一个log文件,log保存producer的产生的数据,producer的数据会被不断的添加到该log文件的末尾,且每条数据都有自己的offset,消费者组中的每个消费者,实时记录自己消费到那个offset了,以便出现错误时,恢复到上次消费的位置继续消费。
log文件采取了分片和索引机制,防止log文件过大导致读取性能问题;
每个partition由多个segment,partition对应的文件名是“topic名字+分区号”文件夹,一个segment对应两个文件,.index文件和.log文件(以当前文件segment的第一条消息的offset命名),.index保存大量的索引信息,.log文件保存数据,.index的索引value对应.log中message的物理偏移量。
如offset=150,根据offset获得对应的segment中.index文件(通过文件名字),然后获得对应.log中message中的物理地址,最后通过物理地址获取message。
2.4 Kafka的高效读写:
a.顺序写磁盘
Producer写的时候,一直是追加到文件的末尾,这样避免大量的磁盘寻址时间。如普通的机械磁盘,顺序写能够达到600M/s,而随机写只有100K/s。
b.零拷贝
零拷贝避免了数据读入到应用程序空间的过程,直接由操作系统的kernel完成读写,极大地提高的效率。
c.分布式并发
kafka采取分布式方式,每个topic数据可以有多个partition,每个partition在不同的broker上,一个partition由一个group中的消费者消费,很大程度上提高了并行度。
3.Kafka的Producer:
3.1 Producer消息的分区选择方式:
1)直接指定partition;
2)没有指明partition时,通过key计算hash值,然后通过partition总个数取模,得到对应的partition值;
3)没有key时,第一次调用时,生成一个随机整数R,然后通过partition总个数取模,得到对应的partition值。以后再次调用时根据R自增。
3.2 Producer消息的可靠性保证:
Producer消息采用acks应答机制,其中leader partition会维护一个ISR(in sync replica set,即和leader partition保持同步的follower partition集合),当ISR中的follower完成数据同步之后,follower向leader发送ack,如果leader 长时间(replica.lag.time.max.ms参数设定)未收到follower的ack应答,那么该follower会被leader踢出ISR;如果leader发生故障,那么会从ISR中选举新的leader。
配置:
Kafka提供三种acks应答机制级别,对数据可靠性进行保证:
1)acks=0:
producer不等待broker的ack,直接返回成功,这样延迟最低,但是当broker还没有写入到磁盘时,broker故障,会导致数据丢失;
2)acks=1:
producer等待broker的ack,leader partition落盘成功之后即返回ack,如果follower还没有同步完成,此时leader故障,会导致数据丢失;
3)acks=-1/all:
producer等待broker的ack,leader partition和follower partition都落盘成功之后即返回ack;如果follower完成同步,broker发送ack之前,broker故障,会导致数据重复。
3.3 Exactly Once:
针对acks=-1会出现重复数据的问题,Kafka 0.11版本引入了幂等性,即不论Producer向kafka集群发布了多少次重复数据,kafka集群只会持久化一条数据。
Exactly Once原理:
Producer在初始化的时候回分配一个PID,发往partition的消息会附带一个Sequence Number,Broker会对<PID,Partition,SequenceNumber>做缓存,保证只会持久化一条,原理更像关系型数据的主键约束。
问题:
PID重启会发生变化,不同的partition有不同的主键,所以幂等性无法保证跨分区会话的Exactly Once。
3.4 partition中的高水位:
follower故障恢复:
当follower故障后,会被踢出ISR中,当follower恢复之后,读取自己的旧的高水位HW,将旧高水位HW之后的数据截取掉,从旧HW从leader同步数据,当follower将LEO同步到操作新HW的时候,即该故障恢复的follower的LEO追上leader之后,该follower可以重新加入到ISR中。
leader故障恢复:
当leader partition故障之后,会从ISR中选出一个新的leader,这时候其余的follower要将各自的高水位HW之后的数据截取掉,然后从新的leader同步数据。
4.Kafka的Consumer:
Consumer采取pull(拉)的方式从broker读取数据,比push(推)更适合不同性能的consumer机器,避免consumer机器的性能故障问题。
针对pull的时候,没有数据的时候,会陷入循环。这时候可以采用长轮询的方式,即通过设置超时时间,consumer会等待一段时间之后才返回。同RocketMQ.
Consumer的分区分配策略:
即决定哪个partition由哪个consumer消费。
1.Range(默认):
...
1.RoundRobin:
...
Consumer的offset的保存:
Kafka 0.9版本之前,consumer默认将offset保存到Zookeeper中,Kafka 0.9版本开始,默认将offset保存在Kafka的内置的topic中,该topic中为__consumer_offset。
Zookeeper的作用:
Kafka集群中有一个broker中会被选举为controller,负责broker的上下线,topic的分区副本分配和leader的选举过程。这些管理工作需要Zookeeper的辅助。
备注:Kafka 2.8已经放弃使用Zookeeper。