技术选型:
使用的是reactor和spring-reactor,本意是替代了多线程之线程池技术。
1、定时任务(3分钟轮询一次)发货失败的记录 ----》 trade服务
2、实时接口,第三方支付回调成功,异步发货 ----》trade服务
这里说下当时的线上错误现象:
用户在平台下单, 等待用户在第三方支付方支付成功, 第三方回调平台trade服务。
如果支付成功,异步开始调用发货方去发货,将订单状态由已支付变更为已发货。由于防止发货出现超时等失败情况,需要定时轮询发货失败的记录进行重试发货。
当时出现发货问题:
部分发货失败,检查发货的URL,能ping通,排除是网络问题。由于是部分成功,部分失败,怀疑是DNS解析不稳定导致。因为这一点猜到,错误地导向了去检查DNS域名解析。
(备注:发生问题的前后, 程序和数模都没发布变更)
排除了不是运维方面的问题后, 逐步检查程序写法是否出问题:
1、多个实时发货和任务调度,虽然是共用java发货代码,但是是不同的reactor事件,固化地认为是多线程机制,每个发货事件之间是无不干扰的才对。
2、由于发货失败是部分的现象,而我们是有任务调度补偿发货机制。怀疑是任务调度没执行。因为在线上不能断点调试,只能在预发环境,对发货任务进行断点调试。任务调度是执行了,只是执行的周期太长。发现了一个特殊的发货URL地址,一直卡着,无法继续断点执行下去。
3、由于发货是去调用第三方发货系统,怀疑是okhttp框架的连接池的可用连接数满了,从而阻塞了后面的请求发货。发现我们并没有手动去设置连接池大小,而框架的连接池大小为2147483647,可以说足够大了,我们的单量远不足于撑爆连接池。
static {
executor =new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp ConnectionPool", true));
}
4、回到第二点,发货卡太久的问题,我们刚开始想的是3秒就会超时,不至于卡住后面的发货。看了下okhttp的配置
http.connectTimeout=100000
http.readTimeout=100000
http.writeTimeout=100000
http.maxIdleConnections=5
http.keepAliveDurationNs=5
超时时间为100秒,而我们发货在失败后,会再重试两次。每笔订单就耗费了3分钟。而当时不通的发货地址有4条。加起来就至少12分钟的阻塞,只有在12分钟后,才会执行后面的实时发货。
但是怎么也解释不了:这4笔订单发货超时,而导致其他的订单也无法去尝试发货,都没有能调用发货请求。
5、当时紧急的解决方案,把4笔订单删掉,后面的任务调度和实时发货就顺畅了。
6、后面在开发环境调试,确定reactor的各个事件是单线程的,默认的dispatcher是SynchronousDispatcher。
这里简单归类下有哪些dispatcher:
public static final String DISPATCHER_GROUP ="dispatcherGroup";
public static final String SHARED ="shared";
public static final String MPSC ="mpsc";
public static final String THREAD_POOL ="threadPoolExecutor";
public static final String WORK_QUEUE ="workQueue";
网友评论