背景
微服务架构商城项目中有一段关于支付功能的业务,逻辑为:
- 修改订单状态,涉及到订单微服务;
- 扣减用户余额,涉及到用户微服务;
- 修改支付单状态,涉及到支付微服务。
分布式事务代码如下,由于这些业务逻辑分布在不同的服务模块之下,修改订单状态和扣减余额通过Open Feign实现,同时使用Sentinel进行服务熔断,在扣减余额的Client编写了服务降级逻辑。
服务降级代码如下:
1 |
|
支付业务代码如下:
1 | // 回滚成功 |
问题
当扣减余额失败时,需要发生回滚:
- 使用@GlobalTransactional(rollbackfor = Exception.class)回滚失效,事务直接提交了;
- 使用@GlobalTransactional、@GlobalTransactional(rollbackFor = Throwable.class)或@GlobalTransactional(rollbackFor = Error.class),均回滚成功;
原因
问题出在 Sentinel 的 SentinelInvocationHandler
类中,对于 fallback
结果的后续处理如下:
Sentinel 会将 Feign Client 抛出的异常进一步封装为 AssertionError
,然后抛出。因此,在调用者中无法通过 Exception
触发回滚,这就是为什么 rollbackFor = Exception
时无法正常回滚。
其他三种情况可以通过 Error
触发回滚,因此正常(默认情况下,rollbackFor
未指定时,未检查异常和 Error
会触发回滚;Throwable
是整个异常体系的顶层接口,包含了 Exception
和 Error
)。
进一步验证该结论,将 rollbackFor
修改为 AssertionError.class
,可以正常回滚:
__END__