部分内容来源:王二Java,月伴飞鱼公众号
基本概念
主题,分区,副本(Leader+Follower)
一个主题可以有多个分区
一个分区可以有多个副本
一个Broker就是一个Kafka节点
单个消费者组消费同一个主题是消息队列模型
多个消费者组消费同一个主题是发布订阅模型
每个消息的唯一offset是不同的(offset值可能相同,但是总体不同),Topic + Partition + Offset 是Kafka中定位一条消息的完整方式
Kafka为什么这么快?
分区设计,消息的吞吐量理论上达到无上限
消息的批量发送,消息的批量消费
消费模式push+pull,拉模式可让消费者根据自身能力进行消费
KRaft模式代替Zookeeper,offset和元数据基于分区分开存储,读写效率更快可存储量更大,Controller副本机制是Controller选举更快
零拷贝实现了快速刷入磁盘
顺序写
数据压缩:Kafka还支持对消息集合进行压缩,Producer
可以通过GZIP
或Snappy
格式对消息集合进行压缩,减少传输的数据量,减轻对网络传输的压力
Kafka的使用场景+优缺点
异步通信+日志同步+实时计算
优点:高性能+高可用+高并发+高拓展
缺点:没有完整的监控工具集
Kafka的消费流程+pull模式的缺点
消费流程:
1.消费者fetch指定分区
2.通过日志的offset定位信息
3.消费者拥有了offset的控制权,可以提交or回滚
Pull的缺点:
如果Broker没有可供消费的消息,将导致Consumer不断在循环中轮询,直到新消息到达。
解决方案:
设置轮询的最大等待时间
它会在拉取信息的时候等待
等5分钟(fetch.max.wait.ms=5000ms)
如果拉取信息的量足够了立即解开阻塞,返回信息
如果拉取信息的量不够,但是阻塞时长到了,就算消息没足够也要返回
负载均衡
轮询
随机
指定Key
自定义策略
重复消费+消费乱序
从生产者+Kafka本身+消费者出发
重复消费
生产者因为网络波动导致Kafka的Broker中有多个相同的信息:
Redis的Zset维护信息唯一ID,消费者防止重复消费
消费者消费时间过长导致offset回滚+offset未提交线程就被kill了:
Redis的Zset维护信息唯一ID,消费者防止重复消费
消费时间间隔值设置大一点,减小最大消费记录的值,调大最大消费时长
开启Kafka事务保证不会重复消费,两阶段提交:
消息组合:PID(生产者ID)+序列号提交,Broker会拒绝重复的 PID + Sequence Number
组合
事务ID的统一提交
消费乱序
网络波动:生产者会因为网络波动导致进入Kafka的信息顺序颠倒,可以加一个顺序增长的ID,然后批量消费的时候进行消息重排序
分区问题:
1、单分区:可以设置topic,有且只有一个partition
2、多分区:根据业务需要,需要顺序的 指定为同一个partition(单独开一个分区,这个分区都是顺序的)
3、多分区:根据业务需要,比如同一个订单使用同一个key可以保证分配到同一个partition上
多线程消费导致的乱序:
让相同业务的信息进入相同的本地线程池队列进行消费,就不会有进入队列不同导致多线程消费出现乱序
可以理解成一个业务维护一个线程,这个业务的信息都在这个线程的队列里
Kafka的消息丢失/不一致
消费者的消息丢失我们有ACK机制确认重试,只要保证ACK提交顺利说明消费就成功了,这就能解决问题了,主要是生产者的消息丢失
可能存在一个消费者提取了一个消息后便提交了 offset,那么还没来得及消费就已经挂了,下次消费时的数据就是 offset + 1 的位置,那么原先 offset 的数据就丢失了
(也就是消费者消费完再提交ACK而不是发送到后自动提交ACK)
这个问题的解决方案也很简单:
「如果是多线程异步处理消费消息,Consumer程序不要开启自动提交位移,而是要应用程序手动提交位移」
生产者利用回调:解决此问题的方法非常简单:Producer永远要使用带有回调通知的发送API,也就是说不要使用producer.send(msg)
,而要使用producer.send(msg, callback)
(消息堆积)生产过程中发生QueueFullException该怎么处理
消息队列到队列满了
1.降低生产速率
2.增加分区数,增加消费者数
3.提高消费者消费效率,业务允许下使用多线程消费
4.如果数据及其重要不能抛弃,我们选择允许生产阻塞而不是删除信息,这样子队列满的情况下生产者可以选择阻塞。或者将未能及时处理的信息落库Mysql人为处理
Kafka的三种分区分配策略
随机分
轮询分
Sticky(Range上的一种升华)
在同组中有新的新的消费者加入或者旧的消费者退出时,不会直接开始新的Range分配
而是保留现有消费者原来的消费策略,将退出的消费者所消费的分区平均分配给现有消费者,新增消费者同理,同其他现存消费者的消费策略中分离
Kafka自动提交存在的问题+消息删除策略解决问题
自动提交存在的问题:
如果你选择的是自动提交位移,那么就可能存在一个问题:
只要Consumer一直启动着,它就会无限期地向位移主题写入消息
此时位移是100,之后该主题没有任何新消息产生,故Consumer无消息可消费了,所以位移永远保持在100。
由于是自动提交位移,位移主题中会不停地写入位移=100的消息
显然Kafka只需要保留这类消息中的最新一条就可以了,之前的消息都是可以删除的
删除策略:
Compact的过程就是扫描日志的所有消息,剔除那些过期的消息,然后把剩下的消息整理在一起
对于同一个Key的两条消息M1和M2,如果M1的发送时间早于M2,那么M1就是过期消息
这个后台线程叫Log Cleaner
很多实际生产环境中都出现过位移主题无限膨胀占用过多磁盘空间的问题,如果你的环境中也有这个问题,建议你去检查一下Log Cleaner线程的状态,通常都是这个线程挂掉了导致的offset占用磁盘空间过多
副本机制的好处
1.「方便实现Read-your-writes」(写入后能马上读)
2.「方便实现单调读(Monotonic Reads)」(单调读保证读数据一致性):
所有的读请求都是由Leader来处理,那么Kafka就很容易实现单调读一致性
Kakfa的保活机制
活锁的概念:消费者持续的维持心跳,但没有进行消息处理(保持连接但没有进行消息处理)。
为了预防消费者在这种情况一直持有分区,通常会利用 max.poll.interval.ms
活跃检测机制
如果调用 Poll 的频率大于最大间隔,那么消费者将会主动离开消费组,以便其他消费者接管该分区
Kafka的拦截器和控制器的作用
Kafka的拦截器:
生产者拦截器允许你在发送消息前以及消息提交成功后植入你的拦截器逻辑
而消费者拦截器支持在消费消息前以及提交位移后编写特定逻辑
Kafka的控制器:创建删除主题,Leader选举,分区重分配,新增删除Broker
Kafka的ACK机制(什么时候消息才算正式提交,可以进行持久化)
0(Leader不需要回复)
1(Leader确认成功)
-1(ISR列表全部同步成功)