生产环境NoHttpResponseException异常排查解决记录分析

 更新时间:2023年10月12日 08:58:53   作者:敲得码黛  
这篇文章主要为大家介绍了生产环境NoHttpResponseException异常排查解决记录分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

业务背景

公司最近正在准备为邮储银行开展一个营销活动,活动规则是:用户使用邮储银行卡在线上支付一分钱,就可以领取50元现金券,卡券领取完毕后,系统会自动退还消费者的1分钱。(相当于免费给邮储用户发放50元现金券),因为发券的入口要做在小程序里面,于是这个需求就落到了C端这边(公司的另一个业务小组),而我主要负责B端支付模块。经过我们商讨后,初步制定的业务逻辑为:用户打开C端小程序进行支付、然后C端将支付请求转给B端支付模块、B端支付模块向微信下单、等待消费者完成支付后B端支付模块通知C端交易完成并返回其支付方式、C端判断支付方式是否为邮储银行卡(是邮储银行卡则发券)、然后C端调用B端支付模块进行退款。为了方便大家理解,我呕心沥血的画出了系统调用的时序图。

生产环境发现的问题

1、NoHttpResponseException导致退款失败

功能上线后,我便开始监控B端支付模块的交易数据,前两天的数据并没有什么异常,支付完成的订单都已经退款完成。然后在第三天快下班时,我又统计了一遍数据,发现竟然存在一笔没退款的订单,我整个人一下子就支棱了起来(不会又写了个Bug吧~),我先在数据库中查到订单号,然后找运维同事拿了一下日志,发现支付回调是正常的,并且下游系统也响应了success,但是却没有调用退款接口进行退款。排查到这里基本已经可以确定不是支付模块这边的问题了,但问题毕竟还是要解决的,于是我联系了C端的同事,暂时先通过接口的方式把消费者的钱进行退款。然后开始排查C端系统的问题,通过C端的日志发现,在请求支付模块进行退款时存在一个异常信息,报错信息如下

看到这个报错,我不禁陷入了思考:C端这个日志表明确实是发起了退款请求,但是B端支付模块根本没收到这个退款请求,这样一来就比较尴尬了,双方系统竟然都没问题,那只能是网络问题了(找不到人背锅,只能推给网络了~~哈哈),刚开始只有一笔,我没怎么在意,过了几天后,陆陆续续发现了好几笔类似的情况,平均几千笔订单就会出现一笔退款失败的,并且这些订单之间毫无规律,搞得我这几天是干啥啥不香,于是痛下决心要深入研究一下这个问题。

2、 异常情况分析

目前能够提供帮助的信息并不多,只有这一个报错日志,通过在网上收集到的一些相关资料,发现了几篇比较有借鉴价值的文章,他们的观点也都几乎一致:服务端主动断开TCP链接,然后客户端使用半断开的链接发起请求时,服务端响应RST包导致此异常情况的发生。 大多数文章的建议是:捕获NoHttpResponseException异常进行重试。

3、验证思路

既然有了上述猜想,那么下一步肯定是要做验证的,验证一下在这个场景下确实会出现此现象。刚开始的验证思路比较简单,就是在服务端通过工具模拟FIN包,然后再用HttpClient继续请求,观察其结果,然而抓包结果显示Httpclient会创建一个新的tcp链接进行请求,木得办法,解铃还须系铃人,恐怕要看一下HttpClient源码才能解释这个现象了。

通过阅读HttpClient源码,大致找到了两个比较关键的逻辑点

  • HttpClient建立tcp链接的时机(三次握手的时机)
  • 发送http请求的时机
tip:在三次握手之前会检查当前tcp链接是否处于Open状态,若处于Open状态则复用此链接,若不处于Open状态则打开一个新的tcp链接,这样一来就解释的通为什么之前HttpClient又重新创建了一个TCP链接的现象了。

4、NoHttpResponseException复现

然后接下来是要做的就是根据之前的猜想来复现NoHttpResponseException场景,具体的思路如下

  • 在Httpclient源码中,等待tcp链接建立完成后,打上断点
  • 等服务器主动发送FIN包断开链接后,再发起请求,然后观察结果

成功复现了NoHttpResponseException现象,抓包结果如下所示

通过抓包结果分析,可以得出"服务端主动断开TCP链接,然后客户端使用半断开的链接发起请求"确实会导致NoHttpResponseException现象,至于服务端什么情况下会主动断开tcp链接?间隔多久主动断开tcp链接?这里就不再讨论了,读者可以自行了解一下keep-alive机制。分析到这里,问题基本上算是解决了,生产环境出现此问题的执行时序应该如下所示

  • 客户端HttpClient复用之前已经Open的链接
  • 然后进行检查(因为此时服务端还未关闭tcp链接,所以链接可用)
  • 紧接着服务端主动关闭链接导致链接不可用
  • 服务端针对客户端的请求响应了RST包

5、解决方案

从业务层面考虑,即使修复了这个问题,也还是会有很大的风险,毕竟网络是未知的,因此我建议C端同事做一个补偿机制,用来处理退款失败情况。

当然网络层面该优化的也得优化,具体步骤是在HttpClient初始化时添加重试策略。

private static CloseableHttpClient init() {
        // 配置请求的超时设置
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
                .setConnectTimeout(CONNECT_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT)
                .build();
        // 重试策略 RETRY_COUNT=3 代表NoHttpResponseException异常重试3次
        HttpRequestRetryHandler retryHandler = (exception, executionCount, context) -> {
            return executionCount <= RETRY_COUNT && exception instanceof NoHttpResponseException;
        }; 
        return HttpClients.custom()
                .setConnectionManager(new PoolingHttpClientConnectionManager())
                .setRetryHandler(retryHandler)
                .setDefaultRequestConfig(requestConfig)
                .build();
    }

6、引发的思考

  • HttpClientPool的链接管理策略(复用、回收等等)。
  • Keep-alive机制
  • 计算机网络

以上就是生产环境NoHttpResponseException异常排查解决记录分析的详细内容,更多关于生产环境NoHttpResponseException的资料请关注脚本之家其它相关文章!

相关文章

  • Spring整合Quartz定时任务并在集群、分布式系统中的应用

    Spring整合Quartz定时任务并在集群、分布式系统中的应用

    这篇文章主要介绍了Spring整合Quartz定时任务并在集群、分布式系统中的应用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-04-04
  • Java设计模式之桥接模式详解(Bridge Pattern)

    Java设计模式之桥接模式详解(Bridge Pattern)

    桥接模式是一种结构型设计模式,旨在将抽象部分与其实现部分分离,从而使两者可以独立地变化,桥接模式通过组合关系代替继承关系,将抽象和实现解耦,使代码更具扩展性和维护性
    2025-02-02
  • Spring JPA之find拓展方法示例详解

    Spring JPA之find拓展方法示例详解

    这篇文章主要为大家介绍了Spring JPA之find拓展方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • 解析Java虚拟机中类的初始化及加载器的父委托机制

    解析Java虚拟机中类的初始化及加载器的父委托机制

    这篇文章主要介绍了Java虚拟机中类的初始化及加载器的父委托机制,包括命名空间等深层次的知识点讲解,需要的朋友可以参考下
    2015-11-11
  • Java实现克鲁斯卡尔算法的示例代码

    Java实现克鲁斯卡尔算法的示例代码

    克鲁斯卡尔算法是一种用于求解最小生成树问题的贪心算法。这篇文章主要为大家详细介绍了Java实现克鲁斯卡尔算法的方法,需要的可以参考一下
    2023-04-04
  • SpringBoot应用jar包启动原理详解

    SpringBoot应用jar包启动原理详解

    本文主要介绍了SpringBoot应用jar包启动原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-03-03
  • java必学必会之方法的重载(overload)

    java必学必会之方法的重载(overload)

    java必学必会之方法的重载,介绍了方法的重载、构造方法的重载,想要学好java方法的重载的朋友一定要好好阅读这篇文章
    2015-12-12
  • Java单例模式分析

    Java单例模式分析

    这篇文章主要给大家介绍了关于Java单例模式,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2021-09-09
  • java使用poi在excel单元格添加超链接设置字体颜色的方法

    java使用poi在excel单元格添加超链接设置字体颜色的方法

    这篇文章主要介绍了java使用poi在excel单元格添加超链接,设置字体颜色,poi功能还是很强大的,基本能想到的功能都能通过poi实现,本文结合实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2023-09-09
  • 详解Spring如何解析占位符

    详解Spring如何解析占位符

    Spring一直支持将属性定义到外部的属性的文件中,并使用占占位符的形式为使用"${}"包装的属性名称,为了使用属性占位符,我们必须配置一个PropertyPlaceholderConfigurer或PropertySourcesPlaceholderConfigurer实例,本文将介绍如何解析占位符
    2021-06-06

最新评论