【不靠谱程序员】接收到回调通知的异步处理
假定我们的支付系统对三方通道回调通知的处理逻辑包括:① 修改本地付款单的付款状态;② 将付款结果调用商户系统API,通知给商户。见下方示意图。
用伪代码来表示,大概是下面这样子:
public void payNotify(Order order) { // ① 修改付款单状态 orderRepository.updatePayResult(order.getId(), "PAY_SUCCESS"); // ② 将付款结果调用商户系统API,通知商户 Merchant merchant = merchantRepository.getById(order.getMerId()); String reqMsg = encrypt("orderNo="+order.getOrderId()+"&state=PAY_SUCCESS"); HttpClient.sendPost(merchant.getNotifyUrl(), reqMsg); }
为了提高程序性能,开发人使用了异步线程,出现了下面的不靠谱代码:
public void payNotify(Order order) { // ① 修改付款单状态 new Thread(()->{ orderRepository.updatePayResult(order.getId(), "PAY_SUCCESS"); }).start(); // ② 将付款结果调用商户系统API,通知商户 new Thread(()->{ Merchant merchant = merchantRepository.getById(order.getMerId()); String reqMsg = encrypt("orderNo="+order.getOrderId()+"&state=PAY_SUCCESS"); HttpClient.sendPost(merchant.getNotifyUrl(), reqMsg); }).start(); }
结果呢,出现了一种情况,当前系统里订单的付款状态没能修改,可是通知商户却成功了。。
我们作为支付服务提供方,我们系统里的付款单不是终态,而商户系统里却是终态。在支付系统中,这样的程序是很要命的。尤其是当我们系统里的付款单是“付款中”而商户系统里是“付款失败”的情况。这时,商户系统可能会因为付款失败而重新发起付款。如果原单实际是付款成功的话,这就出现了资金风险,即,一笔订单付款了两次。就问你怕不怕?
所以,本案中,修改付款单状态的逻辑,必须在主线程执行,确保当前支付系统的付款单状态变更完成后,才可以做后续业务处理。通知商户则可以异步处理,即使通知失败也可以用相关方式来补偿。
这个案例告诉我们,代码中在使用JUC、消息队列、回调函数、消息中间件等提高程序性能的方式进行异步处理时,一定要分清主次,哪些逻辑必须在主线程执行,哪些逻辑可以异步处理。
当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/buguge/p/17739088.html