场景:A服务插入一条记录成功后,调用B服务也插入一条记录,两个操作要同时成功或失败,保持AB服务的事务性
一.设计:
1.增加调用消息服务C,这个服务记录A服务调用B服务的消息,用于存储准执行消息,字段如下:
id:消息id,
param:调用B服务接口的参数
targetUrl:B服务接口地址
uuid:标示这次请求的唯一id,用于判断是否成功执行和幂等,保存在本地执行记录表
checkUrl:A服务验证接口地址,用于验证A服务方法是否成功执行,本地执行记录表中查询uuid,存在即执行了
isOk:标示是否有效需要执行的消息,false表示待执行,true表示可执行
二.调用过程如下:
1.首先A服务的方法设置为本地事务
2.A服务方法先执行插入记录,然后生成uuid插入A本地执行记录表
3.A调用C服务,把uuid,调用B的参数,B的接口地址,校验A是否执行的checkUrl,isOk为false,插入消息表
(备注:这里A服务可以先写入mq,C服务消费mq,这样就是异步的,算是性能优化)
4.插入失败,则throw异常,回滚A服务,如果插入成功则A服务事务成功执行
(备注:可能C服务执行成功,返回的超时异常,造成A服务回滚,这样A没执行,C却记录了消息,这里不用担心)
5.定时任务扫描C服务的消息表isOk为false的,以uuid为参数调用checkUrl地址,去查询A的本地执行表是否有uuid,如果有则说明A成功执行,此C服务消息可执行,设置isOk为true,如果没有查询到uuid,则说明A执行失败,则将此C服务消息记录删除
(备注:此任务命名为:待执行定时任务)
6.定时任务扫描C服务的消息表isOk为true的,以param和uuid为参数,targetUrl为接口地址,调用B服务接口,成功则删除此记录,失败则下次扫描继续调用直到成功
(备注:此任务命名为:可执行定时任务,
这里可以先把C服务扫描的isOk为true的记录写入mq,然后B服务消费mq,这样就是异步的,算是性能优化)
7.B服务接口要设置为本地事务,先用uuid查询B服务的本地执行记录表,如果存在此uuid,说明已经执行过了,直接返回成功;如果没查询到,就将uuid写入B服务本地执行记录表,再执行B服务逻辑
三.说明
这种情况的前提是A服务执行后B服务业务上一定能执行成功,而无需做资源预留的情况
如果你的情况是A执行成功后,B不一定能成功,那么第6步就会无限循环去调用B服务,一直失败
此文只是自己的设计思路,没有实践过