软件介绍:kafka是领英公司基于Scala语言开发的一款消息中间件,后捐献给Apache基金会
主要概念:
Topic 主题 用于区分不同数据,类似于数据库的表
Producer 生产者、source,负责往kafka写入数据的,比如采集数据的软件flume,canal
Consumer 消费者、sink,负责读取消费kafka数据的,比如spark、flink、kafkastreaming,任何一个消费者必须属于一个消费者组,不允许消费者直接消费数据
Broker kafka集群的节点,kafk的节点都叫做Broker,主节点也叫作Controller
Partitioner 划分分区,不同分区可以在不同节点存储,进行并行消费
Replication 副本,每个分区数据,保存多个副本,每个副本必须保存在不同Broker,总数不超过节点数。多个副本有主从之分,leader副本负责读写,follower节点负责从主节点同步数据。由Kafka主节点Controller负责选择主副本。
Segment 分区中数据的划分,用于加快数据检索的效率,将数据按照规则写入不同文件,以后可以根据规则快速的定位数据所在的文件,读取对应的小的segment文件,不用读取所有数据文件
Offset 分区中每条数据的唯一标识,从小到大,是消费者按照offset读取数据,kafka保证消费数据不重复不遗漏的关键,offset是分区级别的概念,每个分区各自管理一套offset,从0开始
ConsumerGroup 消费者组,每个消费者必须属于一个组,以组为单位消费数据,组内多个消费者可并行消费数据,但是单个消费者不能同时消费多个分区数据,只能串行消费它负责的分区。
功能:分布式实时的消息队列
定义:分布式的基于订阅发布模式的高性能高可靠消息队列
高效率读写 kafka数据直接写入pagecache内存(内存使用达到阈值或者达到一定时间比如30s)再写入磁盘,顺序写磁盘,读也是先读pagecache内存,如果pagecache有,则通过零拷贝机制读取,如果pagecache没有,则读取segment index文件使用索引读取
高并发 对数据分区,多个消费者对多个分区消费时,可以进行并行消费
内存数据安全保证 副本机制 分区保存多份,存储在不同节点上
大数据量存储 Kafka写入数据写入的不是普通的内存,是操作系统级别的内存,不会随着kafka的服务关闭或重启而关闭。生产数据发送到主副本分区后,从副本节点会向主副本分区进行同步数据,内存数据占用pageCache(操作系统级别内存)达到10%或者数据写入超过30s ,通过顺序写入机制写入磁盘进行持久化。
高可用:公平架构,zookeeper负责辅助选举和存储Kafka的元数据
集群模式(Kafka只有集群模式)
架构:Kafka集群采用公平架构,每一个节点都有称为主节点的机会,发生故障时会进行故障转移
单点故障:Kafka主节点Controller
数据存储:Kafka管理的分布式磁盘
高可用(HA):Kafka基于Zookeeper实现,日志保存在Zookeeper(新版可以kafka自己实现)
集群启动时:所有节点会尝试在Zookeeper 某个固定目录下注册一个临时节点,注册成功的成为主节点,其他节点会成为从节点,从节点会给该临时节点注册监听。
当主节点挂掉:收到监听的从节点,会尝试去Zookeeper注册该节点,注册成功的成为主节点,其它的成为从节点。从节点给该临时节点注册监听。这时即使主节点重启,也只能成为从节点。
生产数据安全(生产的一次性语义)
丢失问题:
Ack三种级别处理方式
0 :发送不等待ack,直接发送下一条,性能最好,但是数据丢失风险大,一般不用(异步,效率最高,但是数据不能保证不丢失)
1:发送等待写入分区的leader副本后即返回ack,如果leader故障,数据有一定丢失风险,取0和all的折中
all:发送等待写入所有副本都成功再返回ack,无丢失风险,但是性能最差
重复问题:
Ack校验机制,生产者生产数据后,进行Ack校验,如果生产者收到Kafka返回的Ack则继续发送下一条,如果超时没有收到,则进行默认三次的重试。如果数据发送成功,但是返回的Ack丢失,会再次重试发送数据,可能会造成重复。
解决:
幂等性机制:生产者对每条数据进行编号,kafka收到数据后将其和上一条收到的数据的进行比对,如果sequenceID和上一次的sequenceID+1不相等,则直接返回生产者一条ack信息。幂等性机制可以保证数据在生产过程中不重复。(幂等性,类似于mysql的replace into 插入更新命令 ,多次插入和一次插入数据结果不会变化,noSQL数据库都实现了幂等性写入)
消费数据安全(消费的一次性语义)
Kafka通过严格按照offset来消费保证数据的顺序性(分区内)和不重复不遗漏(理论上)
segment:分区数据的更细的划分,按照offset,每写入1G产生一个新的segment,segment包含两种文件,一个是.log文件,是存储的消息数据,另一种是.index文件,存储消息的索引。
消费:
第一次消费:默认是从最新数据开始消费,也可以设置为从最早位置开始消费
后续的消费:从上次最后消费的offset+1的位置开始消费
原则:消费者必须以消费者组的形式进行消费,如果一个组的有多个消费者,则多个消费者消费的数据合起来为完整数据。多个分区多个消费者的情况下,需要遵循消费分配策略来分配。
offset机制
问题:消费者offset为了效率是保存在内存的,如果消费者故障,则offset会丢失,重启之后不知道该从哪里开始消费
kafka将offset保存在__consumer_offset这个Topic中,如果消费者重启,可以从kafka读取上次的offset。offset由消费者进行提交,提交有三种方式,也可以保存在外部存储系统比如Redis或者MySQL等
offset提交kafka方式
1.定时提交(不建议)
设置一个时间间隔,每隔这个时间间隔就提交一次,比如每秒提交一次
缺点:跟消费数据没有直接关联,可能未提交但已消费,也可能已提交但是未消费,消费宕机后,数据可能会重复和遗漏。
2.手动提交(Topic)
每消费处理一个Topic消息,就手动提交一次
优点:比定时提交稍优
缺点:不同分区的消息被不同消费者消费,消费者宕机后,也可能会重复和遗漏
3.手动提交(Partition)(常用)
每消费处理一个Partition消息,就手动提交一次
优点:数据不会遗漏,但有可能重复
4.手动提交(message)
每消费一个消息,手动提交一次
优点,数据不会遗漏,但有极低几率遗漏,性能最差。
生产分区策略:
kafka生产数据有四种分区原则
1.先判断是否有指定分区,如果指定了分区,则放入指定分区
2.如果没有指定分区,再判断是否指定了自定义分区器,如果指定了自定义分区器,则按照自定义分区器分区
3.如果没有指定分区器,在再判断是否指定了Key,如果指定了Key,则按照key的mur值取分区数的余数获取分区编号,放入对应分区
4.如果也没有指定key,则按照默认的粘性分区规则,如果内存中有partition,则继续放入该partition,如果没有,则随机一个分区。
消费分配策略:
一、范围分配策略 RangeAssignor(默认)
按照范围尽量均分,如果不能均分,优先分配编号较小的消费者
优点:如果topic少,分配会比较均衡
缺点:如果topic多,且分区不能均分,会产生负载不均衡问题
问题:数据分配
二、轮询分配策略 RoundRobinAssignor(kafka2.x之前的版本推荐使用)
将各分区进行编号,一个一个分配给消费者
优点:如果有多个消费者,消费的Topic都是一样的,实现将所有分区轮询分配给所有消费者
缺点:遇到消费者订阅topic不一致的,容易产生负载不均衡,如果一个消费者故障,将会重写进行分配。
三、粘性分配策略 StickRobinAssignor(kafka2.x之后推荐使用)
底层采用算法,尽量平均分配消息,求取最优解,执行过程如果某个消费者发送故障,可以直接将其任务平均分配给剩下的消费者,不去改变正在消费的分配。尽量避免进行网络传输
优点:尽量保证所有分配均衡,尽量保证每个消费者如果出现故障,剩余消费者依旧保留自己原来消费的分区
缺点:kafka2.x版本才有
Offset索引设计
offset索引表采用了稀疏索引,采用消息条数偏移量作为key,消息物理存储位置作为value,记录log文件中消息的位置。当消费一个offset时,根据offset,与segment文件名进行比较,segment文件名以当前segment最小的offset偏移量命名。从最后一个segment往前比较,如果比其小,则继续比较上一个,一旦找到比其大的,则根据(查找的offset-segment文件的offset+1)对该文件的index进行检索,采用二分法查找最近偏下的那条数据,再去原表的直接找对应物理地址的数据即可。采用稀疏索引是为了减小索引文件大小,采用从0开始的数据作为key而不选择偏移量存储直接寻址也是为了减少索引文件大小。