记Kafka消费的一次生产故障处理全过程

joytrian
发布于 2023-8-21 16:03
浏览
0收藏

大家好,欢迎来到Tlog4J课堂,我是Jensen。

记录今天发生的一次生产故障以及故障处理全过程。

0x1问题背景

需求背景是这样的:产品要求订单过售后期后,资金平台需要对这些订单进行结算,并把虚拟资产入账到下单客户的虚拟账户。

因为我们是按业务领域拆分多个微服务的,为了解耦订单与资金平台,我们选择了MQ异步消息的方式进行业务数据传递,流程简化如下——

  1. 【订单中心】查询已过售后订单 -> 发送MQ消息给财务中心
  2. 【财务中心】接收MQ消息 -> 校验客户交易数据 -> 调用资金平台结算积分
  3. 【资金平台】结算积分 -> 虚拟资产入账

其中,财务中心MQ消费使用了一个基于Kafka二次封装的组件,默认通过应用内线程池异步消费消息进行业务处理(因为需要在多个地方消费),这个二开的组件也已经用了一年时间,相对较为稳定。

记Kafka消费的一次生产故障处理全过程-鸿蒙开发者社区

OK,到这一步没有发现什么问题。

接下来,不出意外的话马上就会发生意外

凌晨6点触发P1级告警,由于应用内线程池被撑爆,应用走拒绝策略737次,触发SQL慢查询持续10秒(刚好校验客户交易数据操作用到了非索引列查数据库)。

随后进行了问题排查,分析完生产者、消费者端的代码,发现有以下问题:

  1. 消费端财务中心对应的消费方法使用了默认的异步方式处理消息,线程数大小用了默认的200个线程,如果短时间内接收多条MQ而又无法快速执行完释放线程,线程数达到200个必然会走拒绝策略报错,甚至影响其它异步执行MQ的消费者方法(共用了同一个线程池)。
  2. 订单中心同一时刻批量修改已过售后订单,把发送MQ的方法包在了for循环中。这意味着如果同一时刻发送大量MQ消息,又因为第一条消费者存在的隐患,将导致发送的MQ消息无法被正常消费。

0x2处理过程

分析完问题,基本上能确定如何解决了,分三步:

第一步:对于线上消费异常的数据,按照代码逻辑重新跑SQL修复相应数据。这件事需要第一时间做,不能因为程序的问题影响客户体验。

第二步:该MQ组件异步消费的消息堆积能力受线程池大小影响,应该把消息堆积的问题交给专业的MQ自己负责,所以暂时关掉该Topic的异步执行,不用线程池,改为同步。后续对该MQ组件进行优化,不再提供异步执行方式,如使用类似@KafkaListener(topic = "xxx", groupId = "appName.beanName.methodName")的方式,只不过需要动态创建KafkaListener,利用MQ本身消费者组的功能,避免消息堆积在应用线程池内。

第三步:通过业务规避,合理评估需求,对于已经确定的场景,能合并的MQ请求、SQL请求、Feign接口调用请求,比如上面提到的for循环发送订单已过售后通知、校验客户交易数据、资金平台积分入账场景,把它们识别出来,通过批量合并请求的方式解决频繁请求可能发生的问题(以空间换频率)。请求合并后还需要评估合并请求的大小限制,进一步进行请求切割,比如订单合并后有10万条数据,放在一个请求里也不合理,应该按照一定的订单量切割后再发送请求。

这里分享一个领导(技术总监)用了8年的需求分析方法:

记Kafka消费的一次生产故障处理全过程-鸿蒙开发者社区

0x3问题总结

对于此类生产问题,分三步解决:

  • 第一,第一时间修复生产数据,避免影响客户体验;
  • 第二,找出临时解决方案——找出问题根因针对性解决;
  • 第三,长远的问题规避方案——合理评估需求。


文章转载自公众号:架构师修行录

分类
标签
已于2023-8-21 16:03:02修改
收藏
回复
举报
回复
    相关推荐