目录
- 一、概述
- 二、生产者
- 1. 发送原理
- 2. 生产者分区 Partition
- 分区好处
- 分区策略
- 3. 生产者如何提高吞吐量
- 4. 数据可靠性
- ACK应答级别
- 数据不丢失:ACK + ISR
- 数据不重复:幂等性
- 数据有序
- 三、broker
- 1. 工作流程
- 2. 副本相关
- 3. 底层存储
- 4. 高效读写数据
- 四、消费者
- 1. 工作流程
- 2. 分区分配和重平衡
- 3. offset 位移
一、概述
-
定义:是一个分布式的基于发布/订阅模式的消息队列(MessageQueue),主要应用于大数据实时处理领域
-
三大功能
- 削峰: 高峰期的消息可以积压到消息队列中,随后平滑地处理完成,避免突发访问压力压垮系统
- 解耦: 消息队列避免模块之间的相互调用,降低各个模块的耦合性,提高系统的可扩展性
- 异步: 发送方把消息放在消息队列中,接收方无需立即处理,可以等待合适的时间处理
-
基础架构:
组件 | 作用 |
---|---|
Producer | 消息生产者,就是向 Kafka broker 发消息的客户端 |
Consumer | 消息消费者,向 Kafka broker 取消息的客户端 |
Consumer Group(CG) | 消费者组,由多个 consumer 组成。组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响。消费者组是逻辑上的一个订阅者 |
Broker | 一台 Kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个broker 可以容纳多个 topic |
Topic | 消息主题(逻辑概念) ,生产者和消费者面向的都是一个 topic |
Partition | 一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列 |
Replica | 副本。每个分区都有若干个副本,一个 Leader 和若干个Follower |
Leader | 一组副本中的“主”,只有主和生产者消费者交互 |
Follower | 一组副本中的“从”,实时从 Leader 中同步数据,保持和Leader 数据的同步 |
Segment | Partition 物理上被分成多个 Segment,每个 Segment 1个G |
Zookeeper | 保存元信息,现已废除 |
二、生产者
1. 发送原理
涉及到了两个线程——main 线程和 Sender 线程
- 在 main 线程中创建了一个双端队列 RecordAccumulator。main 线程将消息发送给消息队列
- 当消息队列内的消息达到一定大小,或者达到时间限制,会通知sender线程
- Sender 线程不断从消息队列中拉取消息发送到 Kafka Broker
- 可以选择是异步还是同步(同步就是sender等待收到broker的ack后,再去发送新消息)
2. 生产者分区 Partition
分区好处
- 便于合理使用存储资源,可以把海量的数据按照分区切割成一块一块数据存储在多台Broker上。合理控制分区的任务,可以实现负载均衡的效果
- 提高并行度,生产者可以以分区为单位发送数据;消费者可以以分区为单位进行消费数据
分区策略
生产者生产消息的时候:
-
指明partition的情况下,直接将指明的值作为partition值;例如partition=0,所有数据写入分区0
-
没有指明partition值但有key的情况下,将key的hash值与topic的partition数进行取余得到partition值
例如:key1的hash值=5, key2的hash值=6 ,topic的partition数=2,那么key1 对应的value1写入1号分区,key2对应的value2写入0号分区
-
既没有partition值又没有key值的情况下,Kafka采用Sticky Partition(黏性分区器),会随机选择一个分区,并尽可能一直使用该分区,待该分区的batch已满或者已完成,Kafka再随机一个分区进行使用(和上一次的分区不同)。
例如:第一次随机选择0号分区,等0号分区当前批次满了(默认16k)或者linger.ms设置的时间到, Kafka再随机一个分区进行使用(如果还是0会继续随机)
-
自定义分区:定义类实现 Partitioner 接口,重写 partition()方法,方法返回分区号
3. 生产者如何提高吞吐量
- 提高main线程创建的消息队列大小:缓存大一点
- 提高batchsize大小:多等一些数据再传
- 调整等待时间:双刃剑,太短一次传的消息太少,太长有延迟
- 对传输数据做压缩:能传更多的消息
4. 数据可靠性
ACK应答级别
0:生产者发送过来的数据,不需要等数据落盘应答
1:生产者发送过来的数据,Leader收到数据后应答
-1:生产者发送过来的数据,Leader和ISR队列里面的所有节点收齐数据后应答
单纯用0或1都会导致丢数,而单纯用-1会导致多数重复
数据不丢失:ACK + ISR
ACK = -1 + 副本 >= 2 + ISR最小副本数量 >= 2
数据不重复:幂等性
-
数据语义
- 最多一次:ACK = 0
- 至少一次:ACK = -1 + 副本 >= 2 + ISR最小副本数量 >= 2
- 精确一次:幂等性 + 至少一次
-
重复数据的判断标准:具有 <PID, Partition, SeqNumber> 相同主键的消息提交时,Broker只会持久化一条
- PID是Kafka每次重启都会分配一个新的Producer ID
- Partition 表示分区号
- Sequence Number是单调自增的
所以幂等性只能保证的是在单分区单会话内不重复
全局不重复需要开启事务
数据有序
- 生产者有序发送消息
- 一个一个消息的发:一个 Topic 下的同一个 Partition 一定是有序的
- 不是一个一个发:需要开启幂等性且一次发不能超过5个,这样如果乱序到达的话,broker会自己排序
- 消费者有序消费
- 一个分区只让一个消费者来消费,即能保证
三、broker
1. 工作流程
- 生产者将消息发送给分区 Leader
- Leader 将消息写入本地文件
- 对应的 Follower 从 Leader 拉取消息并写入本地文件
- Follower 向 Leader 发送 ACK
- Leader向生产者回复
- leader的维护由保存在paitition内的Controller来做,Controller也是分布式的,他会监听brokers节点的变化,在节点挂掉的时候辅助选举新leader,选举规则:在ids列表内按顺序选择
2. 副本相关
-
定义:每个partition都有多份,叫副本,来提高可靠性
- 副本分为Leader和Follower,只有Leader和生产者和消费者交互
- 副本AR = ISR + OSR
-
Leader 和 Follower 故障处理
- Follower故障:被踢出ISR,恢复后再加入ISR
- Leader故障:从ISR中选出一个新的Leader,恢复后去除旧数据,和新Leader进行同步(只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复)
-
副本分区分配
尽可能的把Leader散开,否则会对某一个broker产生很大的压力
3. 底层存储
partition下进一步将数据分为Segment,每个1G
-
Segment分为
- log:存具体数据,以追加的方式
- index:索引,稀疏索引,4KB记一条索引
- 时间戳:过期删除用的
-
删除方法
- 删除:直接删除
- 压缩:相同key只保留最新的
4. 高效读写数据
- Kafka 本身是分布式集群,可以采用分区技术,并行度高
- 读数据采用稀疏索引,可以快速定位要消费的数据
- 顺序写磁盘
- 页缓存 + 零拷贝技术
- 页缓存PageCache:重度依赖底层操作系统提供的PageCache功能,写的时候直接交给页缓存,读的时候先读页缓存,没有再读磁盘
- 零拷贝:消息从磁盘里读出来之后不走应用层代码,直接走网卡,不占用CPU
四、消费者
1. 工作流程
- 消费者可以分组,一个分区只能由组内的一个消费者消费,消费者组是逻辑上的一个订阅者
- 用offset标识消费的位置,由消费者提交,保存在主题内,由coordinator管理,这也是个分布式
主要就是从broker里拉取数据
2. 分区分配和重平衡
分区分配问题:一个consumer group中有多个consumer组成,一个 topic有多个partition组成,问题是,到底由哪个consumer来消费哪个partition的数据
-
分区分配策略
- Range:对每个 topic 而言, partitions数/consumer数来决定,会产生数据倾斜
- RoundRobin:针对集群中所有Topic而言,所有的 partition轮询分配
- Sticky:尽量均匀地分配分区,根据上次的分配结果尽量减少变动
3. offset 位移
-
位移保存方式:存在__consumer_offsets里,采用 key 和 value 的方式存储数据。key 是 group.id+topic+分区号,value 就是当前 offset 的值
-
位移的提交方式
-
自动提交(可能造成重复消费)
重复消费:已经消费了数据,但是 offset 没提交
比如每隔5s,下一轮过了2s挂了,会重复消费这2s的内容 -
手动提交(可能造成漏消费)
漏消费:先提交 offset 后消费,有可能会造成数据的漏消费
比如消费者取了,还在内存里,刚提交还没来得及落盘就挂了,没落盘的就漏消费了
不管是重复消费还是漏消费,都是提交和落盘的间隙出现宕机的情况,可以开启事务,把这两个动作原子绑定
-