关于异步处理,请分清真正需要异步处理的逻辑
我们的税地系统其中一次需求迭代的开发内容:每次调用银行接口查询订单支付状态时,如果对方返回404-订单不存在,并且如果订单是在5min前创建的,那么,就触发重新下发,要求每笔订单只可重发一次。
展示代码前,下面用类时序图来直观介绍一下这3个类以及实现脉络。
下面贴程序实现代码(含伪代码)。
/** * 银行查单服务类 */ @Service public class BankOrderQueryService { @Autowired private OrderRepeatPayService orderRepeatPayService; public void queryBank(BankOrder order) { // 调用银行接口查询支付结果 String responseMsg = ... BankQueryResult queryResult = JSON.parseObject(responseMsg, BankQueryResult.class); switch ( queryResult.state ) { case 成功: // 支付成功的处理 ... break; case 失败: // 支付失败的处理 ... break; case 404: // 支付单不存在, 触发重发逻辑 ... orderRepeatPayService.repeatPay(order); break; default: // ... break; } } } import org.springframework.scheduling.annotation.Async; /** * 订单重发服务类 */ @Slf4j @Service public class OrderRepeatPayService { /** * 注入订单下发服务bean */ @Autowired private BankOrderPayService bankOrderPayService; /** * 订单重发(只重发一次) */ @Async public void repeatPay(BankOrder order) { if (!canRepeatPay(order)){ log.info("订单不满足重发条件,中止"); return; } //组装参数,调用下发服务,实现再次下发 bankOrderPayService.pay(order); } private static boolean canRepeatPay(BankOrder order){ if(order.createTime <= (当前时间-5min) && redisUtil.setnx("orderRepeatPay:"+order.orderId, "Y", HOUR.toMillis(24))){ return true; } return false; } }
我在review上面代码时,其中,注意到了@Async注解。那么,上面代码有什么不足呢?
主线程方法 BankOrderQueryService#queryBank 每当满足条件state=404时,都会调用标记了@Async的异步方法 OrderRepeatPayService#repeatPay。OrderRepeatPayService#repeatPay里的订单重发的逻辑,并不总是会触发。上面文章开头描述了,每笔订单只可重发一次。所以,不足就显现出来了————JVM会不停地创建线程然后很快释放。如果交易量大,可能会导致线程无法创建(jvm:unable to create new native thread)。
所以,我明确叮嘱小组成员:该用异步的时候再用异步。——异步线程里只处理需要异步处理的代码,主线程里明确判断需要走异步时才走。而不是直接调用异步线程,然后异步线程里判断是否需要执行需要异步处理的代码。——像不像下图的绕口令?
改进后的代码如下:
/** * 银行查单服务类 */ @Service public class BankOrderQueryService { @Autowired private OrderRepeatPayService orderRepeatPayService; public void queryBank(BankOrder order) { // 调用银行接口查询支付结果 String responseMsg = ... BankQueryResult queryResult = JSON.parseObject(responseMsg, BankQueryResult.class); switch ( queryResult.state ) { case 成功: // 支付成功的处理 ... break; case 失败: // 支付失败的处理 ... break; case 404: // 支付单不存在, 触发重发逻辑 ... orderRepeatPayService.repeatPayIfNeeded(order); break; default: // ... break; } } } // import org.springframework.scheduling.annotation.Async; /** * 订单重发服务类 */ @Slf4j @Service public class OrderRepeatPayService { /** * 注入订单下发服务bean */ @Autowired private BankOrderPayService bankOrderPayService; /** * 订单重发(只重发一次) */ public void repeatPayIfNeeded(BankOrder order) { if (!canRepeatPay(order)){ log.info("订单不满足重发条件,中止"); return; } //组装参数,调用下发服务,实现再次下发(异步处理) ThreadPoolUtil.getThreadPoolExecutor().execute(()-> bankOrderPayService.pay(order)); } private static boolean canRepeatPay(BankOrder order){ if(order.createTime <= (当前时间-5min) && redisUtil.setnx("orderRepeatPay:"+order.orderId, "Y", HOUR.toMillis(24))){ return true; } return false; } }
设计图物料:https://www.processon.com/view/link/6579736c3fb4b0188b2d5c09
摘自微信公众号「靠谱的程序员」
当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/buguge/p/17927026.html
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
· 全程使用 AI 从 0 到 1 写了个小工具
2019-12-25 vue项目用npm安装sass包遇到的问题及解决办法
2019-12-25 nginx反向代理配置及常见指令