Kafka重复消费、Dubbo重复调用问题排查

一、业务流程简述:

        本业务为车机流量充值业务,大致流程为:收到微信、支付宝端用户支付成功回调后,将用户订单信息发送至kafka中;消费者接收到kafka中信息后进行解析,处理用户订单信息,为用户订购相关流量包(调用电信相关接口),订购成功/失败后会通过MQTT发送订购成功/失败消息至车机端,若订购失败则为用户退款。

 二、线上问题

8/22 收到400反馈称用户于 8/21购买的流量包未到账

 三、问题排查

  1. 首先排查用户订单记录及订单状态

        可以看到用户在同一时间有两条处理失败的订单,查看代码后发现此订单记录有两处场景会创建:

           1、调用电信套餐订购接口时会根据调用结果创建对应订单记录;

           2、调用自身套餐订购服务失败后会创建对应订单记录。

        看了下订单记录生成逻辑后,我有点蚌埠住了:在自身套餐订购服务内容内调用了电信套餐订购接口并根据订购结果存储对应订单记录后,还要在外层自身订购服务失败后存储一个失败订单,如果订单处理失败的话岂不是最少都有两条失败订单……

        但是我仔细看了下发现这两处创建的订单参数有些许不同,查看数据后发现果然没有这么简单……(竟然有三条订单记录,由于第二种创建的失败订单会有部分参数缺失,页面上不会查询到这类订单)

        其实此时根据订单记录和订单生成逻辑大致可以确定肯定套餐订购服务走了两次,我们查看下服务日志:

        

        确实流量订购失败了,但是二者时间间隔竟然长达40s,而这个项目是用的dubbo服务,查看项目配置后发现设置的调用超时时间为30s,重试次数为2;显然已经超过这个超时时间导致了套餐订购服务重复调用;解决方法也很简单:注解中不允许重复调用即可

@Service(version = "${provider.service.version}", retries = 0)

        到此,可以解决套餐重复订购问题。但是排查服务log时发现这条数据被消费了两次只是因为代码中处理了payLog的状态,而使得第二次消费时没有执行其他逻辑

        kafka消费log

        纳尼,本着对前同事代码极其相信的想法首先想到的是:是不是这款订单微信端回调时调用了两次导致往kafka中发了两条重复数据呢?Ok继续排查发送往kafka中发消息的相关日志:显然支付回调这块逻辑没有问题,只提交了一条消息。

        kafka生产者log

        现在可以确定问题为消费者重复消费,进一步查看订单处理逻辑发现流量订购失败时会有退款操作,但目前用户流量未到账且未退款,猜测应该是充值失败后服务某处出现异常导致消费者没有正确提交位移而引起重复消费;

        查看项目错误日志发现以下信息:

2023-08-21 18:34:46,264 ERROR [org.springframework.kafka.KafkaListenerEndpointContainer#3-0-C-1] o.s.k.l.KafkaMessageListenerContainer [LogAccessor.java : 149] Error handler threw an exception
org.springframework.kafka.KafkaException: Seek to current after exception; nested exception is org.springframework.kafka.listener.ListenerExecutionFailedException: Listener method 'public void com.coocaa.client.web.kafka.KafkaConsumer.orderNotify(org.apache.kafka.clients.consumer.ConsumerRecord<java.lang.String, java.lang.String>) throws java.lang.Exception' threw exception; nested exception is java.net.SocketTimeoutException: connect timed out; nested exception is java.net.SocketTimeoutException: connect timed out
        at org.springframework.kafka.listener.SeekUtils.seekOrRecover(SeekUtils.java:208)
        at org.springframework.kafka.listener.DefaultErrorHandler.handleRemaining(DefaultErrorHandler.java:135)
        at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeErrorHandler(KafkaMessageListenerContainer.java:2707)
        at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeRecordListener(KafkaMessageListenerContainer.java:2588)
        at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeWithRecords(KafkaMessageListenerContainer.java:2457)
        at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeRecordListener(KafkaMessageListenerContainer.java:2335)
        at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeListener(KafkaMessageListenerContainer.java:2006)
        at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeIfHaveRecords(KafkaMessageListenerContainer.java:1375)
        at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.pollAndInvoke(KafkaMessageListenerContainer.java:1366)
        at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.run(KafkaMessageListenerContainer.java:1257)
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.lang.Thread.run(Thread.java:834)

         果不其然,项目中有超时信息具体在MQTT推送消息中,有两处推送服务是因为百度MQTT的新、旧两个版本(此时大致可以确定是之前新老版本同时推送,现在百度不再维护老版本的mqtt引起服务异常)

   mnoIoTPushService.pushSinglePayMessageAction(payLog.getVin(), findOneTimelyResult);
   mnoIoTPushServiceV2.pushSinglePayMessageAction(payLog.getVin(), findOneTimelyResult);

        进一步查看推送服务代码,老版本是使用OkHttpClient(默认超时时间10_000)建立连接,log中是于2023-08-21 18:34:35,656 上报流量订购失败  正常应立马向车机端推送MQTT消息,此处于2023-08-21 18:34:46,264上报连接超时,与OkHttp的10s超时时间基本一致。

 至此问题排查完毕:

  1. 代码逻辑bug,所有订购失败的流量包都会重复生成失败订单;
  2. dubbo设置问题,调用外部接口超过项目设置的dubbo超时时间导致服务重复调用;
  3. 旧代码未及时迭代导致kafka消费者消费异常中断,没有正确提交位移引起重复消费

  • 40
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在生产环境中排查 Kafka 是否存在重复消费可以通过以下几种方式进行: 1. 查看消费者组偏移量:Kafka 提供了一套用于管理消费者组偏移量的 API。你可以通过查看消费者组的偏移量来确定是否存在重复消费。使用 Kafka 提供的命令行工具(如 kafka-consumer-groups.sh)或者编写自定义代码来获取消费者组的偏移量信息。 2. 监控消费消费速度:通过监控消费者的消费速度,可以判断是否存在重复消费。如果消费者的消费速度明显低于生产者的产生速度,可能会导致重复消费。可以通过监控消费者的消费延迟、消费速率等指标来判断是否存在重复消费。 3. 消费者记录去重:在消费者端可以引入一定的逻辑来进行消费记录的去重。可以使用一些唯一标识(如消息的唯一 ID)来判断是否已经消费过该消息。消费者收到消息后,先判断该消息是否已经被消费过,如果已经消费过,则跳过该消息。 4. 监控日志:通过监控 Kafka 的日志可以发现是否存在重复消费的异常情况。可以查看消费者日志中是否有重复消费的记录,以及是否有消费异常等情况。 5. 数据库记录:如果消息被消费后需要写入数据库,可以通过数据库记录来排查是否存在重复消费。可以根据消息的唯一标识在数据库中进行查询,判断是否已经存在该消息的记录。 以上是一些常见的排查方法,根据具体情况选择适合的方式来排查 Kafka 是否存在重复消费

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值