记一次并发引起的问题及排查过程

简介: 聚合支付系统(第四方支付),协议支付模块一直有个小问题。商户调用协议支付接口,该模块会调用下层第三方支付渠道的协议支付服务,如果第三方支付渠道没有同步返回支付结果,则协议支付模块会通过定时任务向第三方支付渠道批量第查询支付结果(每查一笔订单就调一次第三方支付渠道,“批量”相当于并发调用第三方支付渠道)

问题背景


聚合支付系统(第四方支付),协议支付模块一直有个小问题。

商户调用协议支付接口,该模块会调用下层第三方支付渠道的协议支付服务,如果第三方支付渠道没有同步返回支付结果,则协议支付模块会通过定时任务向第三方支付渠道批量第查询支付结果(每查一笔订单就调一次第三方支付渠道,“批量”相当于并发调用第三方支付渠道),大致如下图:


109.png


在支付阶段,基本上没有问题,但是在定时任务批量查询支付结果时,如果查询的订单数量过多,就总会有几个查询请求报 “证书错误”(系统自定义异常,表示请求第三方支付的证书字符串有误)。


定时任务查询支付订单支付结果的逻辑大致是:每隔5秒钟查询当前支付结果是处理中(不是终态——成功或失败的状态)的订单列表,比如有100笔(这100笔订单中可能有一到多个商户发起的支付订单),然后开启100个线程分别调用第三方支付的查询接口进行查询。在调用第三方支付的时候,需要根据订单信息来获取证书字符串。


问题排查


在和第三方支付确认他们收到的证书字符串和对应请求中的证书字符串一致后,就回头看获取证书字符串的相关代码,大致代码如下:

生成证书字符串的工具类:

public class CertUtils {
    private static CertHandler certHandler;
    public CertUtils(String merchantInfo) {
        // 根据商户信息 merchantInfo 生成 certHandler
        // 略
    }
    public String getCertStr(){
        return certHandler.getCertStr();
    }
}

在项目中获取证书字符串(上面工具类的使用)

CertUtils certUtils=new CertUtils(merchantInfo); // ① 根据当前商户信息生成一个工具类的实例
String merchantCert=certUtils.getCertStr(); // ② 通过工具类的实例获得当前商户的证书字符串

不看不知道,一看吓一跳,这工具类用的有点奇葩啊~ 分析下奇葩的原因:


获得证书字符串是由certHandler的getCertStr()方法获得的,而certHandler是根据商户信息生成的,但这里却把certHandler定义为static的,所以即使用的时候new了100个CertUtils实例,CertUtils中引用的CertHandler实例却永远是一个,且指向的对象在不断变化,也就是说,CertUtils中引用的CertHandler实例是公共变量,在多线程环境下可能会出现线程安全的问题。


比如当线程1执行完上面的代码①,准备执行代码②之前,这时线程2也刚好执行完代码①,所以线程2就把线程1生成的certHandler给修改了,线程1执行代码②时获取到的证书字符串其实是线程2的。


分析完毕再具体看日志,正如所料,报“证书错误”的请求所携带的证书字符串,和与它并发发起的那些请求所携带的证书字符串一样:(两个线程发起的请求,红框是商户号不一样,黄色背景是证书字符串一样)


110.png

这就说明,报“证书错误”的请求跟上面分析的线程1情况一样,在执行完上面的代码①,准备执行代码②之前,被其他线程执行了代码①,把公共的CertHandler实例给改掉了,所以拿到的证书字符串也就是别人的。

解决方法:


1、使用上述工具类的时候,对代码①和代码②加锁,可以保证线程安全

2、直接把线程公有的变量certHandler变成线程私有变量,改成非静态的类型


相关文章
|
4月前
|
负载均衡
异步任务处理系统问题之任务去重机制工作的问题如何解决
异步任务处理系统问题之任务去重机制工作的问题如何解决
|
4月前
|
消息中间件 Java 调度
一次线上服务CPU100%的排查过程
文章记录了一次线上服务CPU使用率达到100%的排查过程,通过使用top命令和jstack工具确定了导致高CPU使用的线程,并分析了Disruptor组件的不当配置是问题原因,通过修改组件的策略成功解决了问题。
104 0
|
7月前
|
缓存 JavaScript 前端开发
服务器反应慢如何解决?
通常来说,访问者会在最初的几秒钟内决定是留在您的网站还是离开。如果页面加载时间超过五秒,访问者离开的可能性就会增加 90%。所以,作为站长们,必须减少服务器响应时间,以确保其网站加载速度更快。以下是减少网站服务器响应时间的几种简单方式。
142 19
|
SQL 前端开发 测试技术
一次纯线上接口异常的排查过程
一次纯线上接口异常的排查过程
152 0
|
7月前
|
测试技术
如何避免测试同化现象?
如何避免测试同化现象?
|
Java Linux
生产环境OOM问题的排查记录
生产环境OOM问题的排查记录
198 1
|
缓存 测试技术 数据库
软件测试面试题:假设在测试过程中某些事务的响应时间过长,但分析应用服务、数据库以及网络都属于正常现象,问题可能出现的原因有哪些?
软件测试面试题:假设在测试过程中某些事务的响应时间过长,但分析应用服务、数据库以及网络都属于正常现象,问题可能出现的原因有哪些?
376 0
|
缓存 监控 数据库连接
CPU飙高排查方案与思路
当CPU飙高时,可能是由于程序中存在一些性能问题或者死循环导致的。以下是一些排查CPU飙高的方案和思路
890 0
|
消息中间件 运维 监控
线上踩坑记:项目中一次OOM的分析定位排查过程!
线上踩坑记:项目中一次OOM的分析定位排查过程!
|
关系型数据库 Java 应用服务中间件

相关实验场景

更多