生产环境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的资料请关注脚本之家其它相关文章!

相关文章

  • java 移动文件,并修改名称方式

    java 移动文件,并修改名称方式

    这篇文章主要介绍了java 移动文件,并修改名称方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 几句话说清session,cookie和token的区别及说明

    几句话说清session,cookie和token的区别及说明

    这篇文章主要介绍了几句话说清session,cookie和token的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • 基于SpringBoot创建Web页面并热更新的操作步骤

    基于SpringBoot创建Web页面并热更新的操作步骤

    SpringBoot是一个用于快速开发单个微服务的框架,它基于 Spring 框架,简化了Spring应用的初始化过程和开发流程,本文给大家介绍了如何基于SpringBoot创建Web页面并热更新,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2023-11-11
  • Javafx实现国际象棋游戏

    Javafx实现国际象棋游戏

    这篇文章主要为大家详细介绍了Javafx实现国际象棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • Springboot使用cache缓存过程代码实例

    Springboot使用cache缓存过程代码实例

    这篇文章主要介绍了Springboot使用cache缓存过程代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • java金钱处理方法实例详解

    java金钱处理方法实例详解

    这篇文章主要介绍了java金钱处理方法实例详解的相关资料,这里提供实现方法分转化成元的实现方法,需要的朋友可以参考下
    2017-08-08
  • 深入学习JavaWeb中监听器(Listener)的使用方法

    深入学习JavaWeb中监听器(Listener)的使用方法

    这篇文章主要为大家详细介绍了深入学习JavaWeb中监听器(Listener)的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • 如何用idea数据库编写快递e站

    如何用idea数据库编写快递e站

    这篇文章主要介绍了如何用idea数据库编写快递e站,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-01-01
  • Java获取电脑真实IP地址的示例代码

    Java获取电脑真实IP地址的示例代码

    这篇文章主要介绍了Java如何获取电脑真实IP地址,忽略虚拟机等IP地址的干扰,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-09-09
  • 图文详解Java线程和线程池

    图文详解Java线程和线程池

    下面小编就为大家带来一篇详谈Java的线程和线程池。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-11-11

最新评论