kafka
kafka
定义:kafka是一个开源的分布式事件流平台,被数千家公司用于高性能数据管道、流分析、数据集成、关键任务应用。传统的消息的队列主要的应用场景:缓存/削峰,解耦,异步通信.
消费队列的两种模式
- 点对点的模式:消费者主动拉取数据,消息收到后清除消息
- 发布订阅模式: 可以有多个topic主题,消费者消费数据后,不删除数据。每个消费者相互独立,都可以消费到数据。
基础架构
- 生产者Producer:消息生产者,就是kafka broker发消息的客户端。
- kafka集群:
- Broker: 一台kafka服务器就是一个broker,一个broker可以容纳多个topic。
- topic: 可以理解为一个队列,生产者消费者面向一个的都是一个topic。
- partition: 一个非常大的topic可以分布到多个broker,一个topic可以分为多个partition,每个partition是一个有序的队列。
- replica: 一个topic每个分区都有若干个副本,一个leader和若干个follower;
- 消费者(组)Consumer(Group):消费者组,由多个consumer组成,消费者组内的每个消费者负责消费不同分区的数据,一个分区只能由一个组内的消费者消费;消费者组之间互影响。
常用命令
- 查看所有topic:
bin/kafka-topics.sh --boootstrap-server 192.168.52.0:9092 --list
(--create) (--describe) (--alter 分区只能增加不能减少) (--delete) - 查看生产者:
bin/kafka-console-producer.sh --boootstrap-server 192.168.52.0:9092 (--from-beginning) --topic first
生产者-Producer
在消息发送过程中,涉及到两个线程,main
线程以及sender
线程,在main线程创建了一个双端队列RecordAccumulator
。main线程将消息发送给RecordAccumulator,Sender线程不断从RA中拉取数据发动给kafka broker
key.serializer value.serializer | buffer.memory | batch.size | linger.ms | acks | retries | compression.type |
---|
- 指定发送消息的key value序列化类型,要写全类名
- 缓冲区总大小,默认32M
- 缓冲区一批数据量最大值,默认16K
- 如果数据迟迟未达到batch.size ,sender等待linger.time之后就会发送数据。
- acks:0生产者发送过来的数据不需要等数据落盘应答;1leader收到数据后应答;-1 (默认)leader和isr队列里面的所有节点收齐数据后应答。
- 消息发送出现错误时,系统会重发消息,表示重试次数,默认int最大值
- 生产者发送的所有数据的压缩方式,none(默认)。
生产者分区的好处
- 便于合理使用存储资源,每个partition在一个broker存储,可以把海量的数据按照分区切割成一块一块的数据存储在多台broke上,合理控制分区的任务,可以实现负载均衡
- 提高并行度,生产者可以以分区为单位发送数据,消费者以分区消费数据
- 指明partition的情况,直接将指明的值作为partition值
- 没有指明partition值,但有key的情况下,将key的hash与topic的partition取余得到partition值(顺序消费)
- 既没有partition又没有key值,采用粘性分区器,会随机选择一个分区,并且尽可能一直使用该分区,待该分区batch已满或者已完成,再随机一个分区进行使用
生产经验
- 生产者如何提高吞吐量
- 数据可靠性:
ack
应答级别
0:可靠性差,效率高 ,可能丢数 ;1:应答完成后还没开始同步副本,leader挂了,新的leader不会收到信息,可能丢数; -1:可靠性高,效率低,不会丢数,但是若有一个follower因为故障不能与leader同步? 另外数据重复?
leader维护了一个动态的in-sync replica set
(ISR) 意味和leader保持同步到follower+leader集合,如果follower长时间未向leader发送通信请求或者同步数据,则该follower将被提出ISR,这样就不用等长期联系不上或者已经故障的节点
同步判断条件:follower向leader拉取数据的间隔小于replica.lag.time.ms=10000
数据完全可靠性条件=ack级别设置为-1 + 分区副本>=2 +ISR 里应答的的最小副本数量>=2
- 数据重复
至少一次(at least once) =数据完全可靠性条件,保证数据不丢失,可能会重复
最多一次(at most once) =ack级别为0 ,保证数据不重复,可能会丢失
精确一次(exactly once) ? 幂等性+至少一次幂等性原理
幂等性就是指producer 不论向broker发送多少次重复数据,broker端只会持久化一条,保证单分区单会话不重复,具有<pid(每次重启都会分配一次),partition,seq number(单调递增)>
相同主键的消息提交时,只会持久化一条
每个producer 在初始化时会生成一个producer_id ,并为每个目标partition委会一个序列号; producer每发送一条消息,会将对应的序列号加一,broker端会维护一个序列号,对于每收到的一条消息,会判断 服务端的SN_old 和接受到的消息中的SN_new 进行对比
如果old+1=new 正常;old+1>new 重复写入数据直接丢弃;old+1<new 说明中间有数据尚未写入或者发生乱序,或者数据丢失,抛出异常 OutOfOrderSequenceException
- 数据有序
单分区有条件有序: 未开启幂等性max.in.flight.requests.per.connection=1
;开启幂等性max.in.flight.requests.per.connection<=5
; 启用幂等性后,kafka会缓存producer发来的最近的5个request元数据,无论如何最近5个都是有序的,开启幂等性切缓存请求小于等于5个,会在服务端重新排序。
多分区无序
生产者事务
transaction coordinator
(事务协调器) :开启事务,必须开启幂等性。producer在使用事务功能之前,必须先自定义一个唯一的transactional id 即使客户端挂掉,他重启后也能继续处理未完成的事务。
kafka broker
工作流程
- broker (每个kafka是一个broker)启动后在zk中注册
/brokers/ids/ [0,1,2]
- controller(每个broker都有一个controller) 谁先注册谁说了算,由选举出来的controller 监听broker节点的变化 c
ontroller "brokerid": 0
- controller 决定leader选举,选举规则:在ISR中存活为前提,按照在AR(kafka分区所有副本的统称)中排前面的优先
- controller将节点信息上传到zk中,其它controller从zk中同步相关信息
/brokers/topics/first/partitions/0/state "leader":0, "isr":[0,1,2]
- 假设broker1中leader挂了。controller监听到节点变化,获取ISR
- 选举新的leader (在ISR中存活为前提,按照AR中排在前面的优先) 更新leader以及ISR
Kafka判断一个节点是否活着有两个条件
1. 节点必须可以维护和ZooKeeper的连接,Zookeeper通过心跳机制检查每个节点的连接。
2. 如果节点是个follower,他必须能及时的同步leader的写操作,延时不能太久。
kafka 副本--提高数据可靠性
kafka默认副本一个,生产环境一般配置2个,保证数据可靠性;太多副本会增加磁盘存储空间,增加网络上数据传输,降低效率
kafka分区中所有副本的统称为AR,AR=ISR+OSR
,ISR表示和leader保持同步的follower,OSR表示follower与leader副本同步时,延迟过多的副本。
leader选举流程
kafka中有一个broker的controller会被选举为controller leader,负责管理集群broker的上下线,所有topic的分区副本分配和leader选举等工作。
leader 和follower故障细节处理
LEO (Log End Offset):每个副本的最后一个offset,最新的offset+1
HW (High Watermark):所有副本中的最小的LEO
LSO (last stable offset):一个事务批次中事务头消息的前一条
HW 用途:消费者只能看到HW之前的数据;leader挂掉,其它同步follower成为新leader要截断HW之后的消息
- follower故障 : follower发生故障后会被临时提出ISR,这个期间Leader 和Follower 继续接受数据 ,待该follower恢复后,follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截掉,从HW开始向leader进行同步、等该follower的LEO大于等于该partition的HW,就可以重新加入ISR
- leader 故障: leader发生故障之后,会从ISR中选出一个新的leader,为保证多个副本的数据一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader同步数据。
分区副本分配
生产经验-leader partition 负载平衡
正常情况下,kafka本身会自动把leader partition均匀分散在各个机器上,来保证每台机器的读写吞吐量都是均匀的。但是如果某些broker宕机,会导致过于集中在其他少部分几台broker上,回导致少数几台broker的读写请求压力过高,读写请求很低,造成集群负载不均衡。
文件存储机制
kafka文件存储机制:topic 是逻辑上的概念,partition是物理上的概念,每个partition对应于一个log文件,该log文件存储的就是producer生产数据,producer生产数据会被不断追加到该log文件末端,为防止log文件过大导致数据定位效率低下,kafka采取了分片和索引机制,将每个partition分为多个segment ,这些文件位于一个文件夹下,该文件夹的命名规则为 :topic+分区序号
.index | .log | .timeindex |
---|---|---|
偏移量索文件 | 日志文件 | 时间戳索引文件 |
index为稀疏索引,大约每往log文件写入4kb数据(log.index.interval.bytes),会往index文件写入一条索引; index 文件中保存的offset为相对offset,这样确保offset的值所占空间不会过大,因次能将offset的值控制在固定大小
文件清理策略
kafka默认的日志保存文件为7天,日志一旦超过设置时间,提供了两种日志清理策略:
- delete 日志删除: 将过期数据删除
- compact 日志压缩: 对于相同的key不同value值,只保留最后一个版本,压缩后的offset可能是不连续的
高效读写数据
- kafka本身是分布式集群,可以采用分区技术,并行度高
- 读取数据采用稀疏索引,可以快速定位到要消费的数据
- 顺序写磁盘
- 页缓存+零拷贝 : kafka的数据加工处理操作交由kafka生产者和kafka消费者处理, broker应用层不关系存储的数据,不用走应用层,传输效率高。 kafka重度依赖底层操作系统提供的pagecache功能,当上层有写操作时,操作系统只是将数据写入pagecache。当读操作发生时,先从pagecache查找,查找不到,再去磁盘中读取。
零拷贝:减少CPU拷贝次数,Customer从broker读取数据,采用sendfile(),将磁盘文件读到OS内核缓冲区后,直接转到socket buffer进行网络发送。通过DMA技术将文件内容复制到内核模式下的read buffer,不管有没有数据复制到socket buffer ,只有包含数据的位置和长度的信息的文件描述符被加到socket buffer,DMA引擎直接将数据从内核模式传递到网卡设备。
kafka消费者
消费方式:pull
工作流程
消费者组(Consumer Group
): 由多个consumer组成,形成一个消费者组的条件是所有消费者的groupid相同
- 消费者组内的每个消费者负责消费不同分区的数据,一个分区只能由一个组内的消费者消费。
- 如果向消费者组内添加更多的消费者,超过主题分区数量,则有一部分消费者就会闲置,不会接受任何消息。
消费者组初始化流程
coordinator
:辅助实现消费者组的初始化和分区分配 ,节点选择:groupid的hashcode值%50 (_consumer_offsets
的分区数量) 在哪个broker上就选择这个节点的coordinator作为这个消费者组的老大,消费者组下的所有消费者提交offset时候就往这个分区提交offset- 选出一个consumer作为leader ,把要消费的topic情况发送给leader消费者,leader会制定消费方案,把消费方案发送给coordinator
- coordinator就把消费方案下发给各个consumer, 每个消费者和coordinator保持心跳(3s默认),一旦超时,该消费者会被移除,并触发再平衡,或者消费者处理消息时间过长,也会触发再平衡。
生产经验--分区的分配以及再平衡
- 一个consumer group有多个consumer组成,一个topic有多个partition组成,那由那个consumer来消费哪个partition数据
- kafka 有四种主流的分区分配策略:
Range、RoundRobin、Sticky、CooperativeStickty
, 默认策略是Range+CooperativeSticky ,可以同时使用多个分区分配策略
- Range是针对每个topic而言的,首先对同一个topic 里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序。 通过partition/consumer 来决定每个消费者应该消费几个分区 ,容易产生数据倾斜。
- RoundRobin针对集群所有topic 而言,把所有partition和所有consumer列出来,按照hashcode进行排序,最后通过轮询算法分配给partition给各个消费者
- Sticky:在执行一次新的分配之前,考虑上一次的分配结果,尽量少的调整分配的变动,可以节省大量的开销。
提交offset
- 自动提交offset
- 手动提交offset :提供两种,分别是同步提交和异步提交,两者的相同点是都会将本次提交的一批数据最高的偏移量提交;不同的是同步提交阻塞当前线程,一直到提交成功,并且会自动失败重试,而异步提交则没有失败重试机制,有可能提交失败。
漏消费和重复消费
重复消费:已经消费了数据,但是offset没提交 (自动提交offset引起)
漏消费:先提交offset后消费,有可能会造成数据的漏消费(手动提交offset)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix