[1.1 如何保证消息不丢](#1.1 如何保证消息不丢)
[1.2 如何保证消息不重](#1.2 如何保证消息不重)
[2.1 线上积压排查思路](#2.1 线上积压排查思路)
[2.2 增加消费能力](#2.2 增加消费能力)
Q1:消息可靠性(不重不漏)
理解可靠性前。介绍消息语义,即消息传递的标准
标准 | 丢失 | 重复 | 适用场景 |
---|---|---|---|
At most once(至多一次) | 会丢失 | 不会重复 | 高并发高吞吐,允许消息丢失,如日志收集 |
At least once(至少一次) | 不会丢失 | 会重复 | |
Exactly once(恰好一次) | 不会丢失 | 不会重复 |
1.1 如何保证消息不丢
从各个层面分析
- 参照:《深入理解kafka-核心设计与实践原理》第8.3章 + 自己理解
层面 | 事项 |
---|---|
生产者(客户端) | * 代码发送层面 * **①发送消息Api的三种模式:**发后即忘、同步和异步。发后即忘会丢失消息,后两者能知道是否发送成功 * **②补发消息机制:**客户端持久化消息,支持消息的补偿 * 配置层面 * **③生产者客户端参数(acks参数):**acks= -1最可靠,其余两种都可能会丢失消息 * **④重试参数(retries参数和retry.backoff.ms参数):**对于可重试的异常(如网络抖动),通过失败重试机制重发消息 |
Broker服务端 | 服务端需要注意的都是配置参数层面 * ①副本数(replication.factor参数) > 1 * 越多的副本数越能够保证数据的可靠性,但副本太多会影响性能。应对宕机风险,通常设置为3就能满足大多数场景 * ②ISR集合中的最小副本数(min.insync.replicas参数) >1 * 用于辅助配合acks=-1来使用:leader副本的消息流入速度很快,而follower副本的同步速度很慢。在某个临界点时所有的follower副本都被剔除出了ISR集合,那么ISR最终只剩一个leader副本,导致acks=-1演变为acks=1的情形 * 该值必须小于副本数,一个典型的配置是:replication.factor=3,min.insync.replicas=2 * **③是否允许从非ISR中选举新leader(unclean.leader.election.enable参数) = false:**默认为false,不允许 * 非ISR集合中,可能还未同步所有消息,设置成true会造成消息丢失 * 其他:log.flush.interval.messages和log.flush.interval.ms,是否开启同步刷盘(默认是不做控制而交由操作系统本身来进行处理) * 可能性分析:由于刷盘是操作系统控制,理论上讲会存在即使kafka写入成功,但由于机器宕机,操作系统未及时刷盘,丢失数据的风险 * 但同步刷盘非常损耗性能,改成每次提交时手动同步刷盘对性能影响太大。这里不建议修改此配置,因为发生可能性很小 |
消费者(客户端) | **①自动提交(enable.auto.commit参数) = false:**默认为true,自动提交。不再介绍,见3.2.5节 * 自动提交会带来重复消费和消息丢失的问题:消费者还未执行完业务流程,异步线程已对服务端发起了提交 * 手动提交需遵守一个原则:如没有消费完,则不能提交 |
1.2 如何保证消息不重
消息重复无法避免,只能在消费者消费时过滤重复消息。可能的重复原因如下:
根因 | |
---|---|
生产者 | ①业务方代码原因:对同一份消息内容发送了多次 ②网络问题:生产端发送消息后,服务端已经收到消息了,但是假如遇到网络问题,无法获得响应,生产端就无法判断该消息是否成功提交到了 Kafka,而我们一般会配置重试次数,但这样会引发生产端重新发送同一条消息,从而造成消息重复的发送 |
Broker服务端 | 服务端不会重复存储消息,如果有重复消息也应该是由生产端重复发送造成的 |
消费者 | 分区重分配引起 * 在客户端实例变化、分区扩容、集群切换等场景会涉及到分区重分配,由于新老客户端感知分配结果不及时,可能出现两台机器短时间消费同一个分区的场景,造成重复消费 |
备注:分区重分配,与再均衡(rebalance)的区别
- 重分配:作用在broker层面,是broker与partition间的分配;通常是为了管理优化集群手动触发
- 再平衡:作用在消费组层面(新增/下线消费者),Rebalance的目的是确保分区在consumer group成员之间平均分配,以便每个consumer都有分区去消费;通常是Kafka协调器自动触发的
处理思路:幂等
从对系统的影响结果来说:At least once + 幂等消费 = Exactly once。
-
利用数据库的唯一约束实现幂等
-
记录消费结果并检查操作
Q2:积压/消费能力
2.1 线上积压排查思路
是什么引起的积压:
-
先止损,快速定位积压的原因
-
上游生产流量突增
-
上游流量没变
-
不消费了:
-
消费者是否下线了?
-
某一个partition的offset卡住了?
-
-
还在消费:消费能力下降?
-
-
2.2 增加消费能力
搞清原因:什么导致【消费速率 < 生产速率】
生产速率 | 可能原因 | 解决方案 | 备注 |
---|---|---|---|
生产速率不高 | 自身业务消费链路长,导致消费慢 | 优化消费链路 | 绝大多数情况 |
生产速率高 | ①消费者实例 < partition数量。一台机器消费多个partition的情况,并且机器负载较高 | 扩集群机器,一直到【消费者:partition=1:1】 | 影响可控 |
生产速率高 | ②消费者实例:partition = 1:1,但消费速率仍小于生产速率 | 扩partition。但一未扩partition会增加服务端负载 | 影响可控 |
生产速率高 | ③partition本身已很多,但生产速率实在太大 | 增加单partiton并行消费线程数目(这种方式不保证消费顺序!!!) * mafka和rocketmq支持,kafka与rabbitmq不支持 * 总体思路:滑动窗口 | 是不是该考虑架构与技术选型的问题了? |
生产速率高 | ④生产速率实在太大了 | pushServer | 是不是该考虑架构与技术选型的问题了? |