一,背景
新需求来了,在旧代码上处理新需求,今天就来刨一刨同事小伙的屎山。
二,代码逻辑
如图所示,系统若干服务的操作影响的数据要根据类型推送到下游服务B、C、D等,服务O是做专门转发的服务,根据策略进行转发。
三,分析
1,服务A和O的交互参数
交互参数不是实体,而是业务主键ID,组装实体的逻辑不是放在服务A侧,而是放在O里,这样做的原因是方便重试,见下图
如果推送不成功,左侧的补偿程序会执行,这个时候只需要传业务主键及类型即可触发。
但是,但是,但是
没考虑数据的时态,因为组装参数是以执行瞬间数据库的数据为准的,在这样的场景下就有问题:
| d-1 | d -1 | d |
|---|---|---|
| 推送账单下单失败 | 用户支付,更新状态 | 重试推送状态不应该是支付,应是下单 |
怎么办,改呗,组装参数增加重试时间这个参数,,,,,问为啥之前没出问题,因为这个项目只到提交测试阶段,我就接手了,,,,,,,,
2,事务问题
我把A、O写成代码,就一目了然了
class AService {
@Resource
private Oservice oService;
@Transaction(Rollback = Exception.class)
publict void deal(){
String bizNo = "xxx";
//xxxx业务逻辑
oService.sendB(bizNo);
CompletableFuture.supply(()-> oService.sendC(bizNo));
}
}
看出问题来了么,看出几个问题了?
当前事务没有提交,oSerivce里sendB、sendC组装参数查不到数据,不管是同步调用还是异步调用,都没办法读取当前事务未提交的数据,那如何处理? 改MQ,确实,还解决了分布式事务问题,由于sendB、sendC属于通知类操作,和当前操作的优先级相比,输入不入流的小操作,当然不能没有,所以用MQ,或者本地事务表都能解决,当然本地事务表要配合扫描定时任务。
如图,操作和写本地事务表在一个事务里,发送交由重试任务来解决。