一,分布式事务传播行为
调用链描述
一个普通事务注解的方法,调用一个分布式事务注解方法
分布式事务注解方法:包含一个本地更新,和两个外部服务更新操作,涉及三个服务
问题
1,普通事务注解方法,在全局事务问题中,是否会生效回滚@答案是会回滚
原因
1,分布式事务注解,抽离到另外一个类中,不会出现AOP失效的问题
2. 事务传播机制
FulfillOrderBizService.userCancel 使用 @Transactional 创建本地事务
当调用 OutboundSellOrderBizService.userCancel 时,由于是通过代理对象调用,@GlobalTransactional 能够正常工作
Seata的分布式事务会接管整个事务管理,包括之前由 @Transactional 开启的本地事务
3. Seata的工作机制
Seata的 @GlobalTransactional 注解具有以下特点:
一旦开启全局事务,会覆盖本地事务管理器
能够管理跨服务的事务操作
在调用链中传播全局事务上下文
代码
@Transactional(rollbackFor = Exception.class) @XLock(prefix = "OUT_BOUND_SELL_ORDER_USER_CANCEL", keys = {"#orderNo"}, leaseTime = 5L, waitTime = 5L) public void userCancel(String orderNo) {
@GlobalTransactional(rollbackFor = Exception.class) public void userCancel(OutboundSellOrderCancelParam param) {
@Transactional(rollbackFor = Exception.class)@XLock(prefix = "OUT_BOUND_SELL_ORDER_USER_CANCEL", keys = {"#orderNo"}, leaseTime = 5L, waitTime = 5L)public void userCancel(String orderNo) {// 更新本地1fulfillOrderService.updateById(new FulfillOrderParam().setId(fulfillOrderBO.getId()).setStatus(FulfillOrderStatusEnum.CANCELED.getCode()));// 调用分布式事务注解方法outboundSellOrderBizService.userCancel(cancelParam);}@GlobalTransactional(rollbackFor = Exception.class)public void userCancel(OutboundSellOrderCancelParam param) {// 更新本地2,抽离到另外一个类,不需要加注解OutboundOrder outboundOrder = outboundOperateService.userCancel(param);// 更新远程服务 取消配送和取货服务cancelDeliveryAndReceiveOrder("C端用户取消", param, outboundOrder);}/*** 取消配送和取货服务*/private void cancelDeliveryAndReceiveOrder(String operator, OutboundSellOrderCancelParam param, OutboundOrder outboundOrder) {// 更新取货服务boolean cancelResult = receiveOrderService.cancelOrder(new OrderParam().setOutOrderCode(outCode).setOperationUser(param.getOperationUser()).setOperationEnterprise(param.getOperationEnterprise()));// 更新配送服务CancelDeliveryBO cancelDeliveryBO = deliveryService.cancel(new CancelDeliveryParam().setLogisticsNo(outboundOrder.getLogisticsNo()).setCancelTime(new Date()));}// 更新本地2,抽离到另外一个类,不需要加注解@Overridepublic OutboundOrder userCancel(BaseOutboundOpParam param) {
疑问及测试说明
* todo 分布式事务问题 * order 是否需要回滚 * 取消出库单失败,回滚 * 取消配送,是否回滚 @回滚 * FulfillOrderBizService.userCancel 方法中的本地事务会覆盖掉 OutboundSellOrderBizService.userCancel 方法的分布式事务特性 * @实际不会覆盖。应该配送失败,取货是否回滚,判断分布式事务 @回滚 */
二,內部调用导致事务失效解决
需求描述
批量取消配送单,可部分成功,部分失败。
调用链:wms 分别调用配送中心,取货中心
实现目标
可以实现独立事务
可以实现分布式事务
代码
public List<OutboundSellOrderCancelVO> cancel(OutboundSellOrderCancelForm form) {List<OutboundSellOrderCancelVO> results = new ArrayList<>();for (String outCode : form.getOutCodes()) {try {// 每个订单独立事务处理OutboundSellOrderCancelVO result = self.processSingleOrderInTransaction(outCode, form);results.add(result);} catch (Exception e) {log.error("取消分配 失败, 出库单号: {}, 错误信息: {}", outCode, e.getMessage(), e);OutboundSellOrderCancelVO failureVO = new OutboundSellOrderCancelVO();failureVO.setOutCode(outCode);failureVO.setErrorMessage(e.getMessage());results.add(failureVO);}}log.info("取消分配 完成,总数: {}, 成功: {}, 失败: {}",form.getOutCodes().size(),results.stream().filter(vo -> StringUtils.isBlank(vo.getErrorMessage())).count(),results.stream().filter(vo -> StringUtils.isNotBlank(vo.getErrorMessage())).count());return results;}/*** 每个订单独立事务处理*/@GlobalTransactional(rollbackFor = Exception.class)public OutboundSellOrderCancelVO processSingleOrderInTransaction(String outCode, OutboundSellOrderCancelForm form) {OutboundSellOrderCancelParam param = new OutboundSellOrderCancelParam();param.setOutCode(outCode);param.setOperationUser(form.getOperationUser());param.setOperationEnterprise(form.getOperationEnterprise());// 1. 取消销售出库单OutboundOrder outboundOrder = outboundOperateService.cancelDelivery(param);param.setExternalCode(outboundOrder.getExternalCode());// 2. 取消配送单、取货单cancelDeliveryAndReceiveOrder("B端取消", param, outboundOrder);// 返回成功结果return new OutboundSellOrderCancelVO().setOutCode(outboundOrder.getOutCode());}
疑问及测试说明
/*** 事务生效问题* processSingleOrderInTransaction self* self @回滚* not self @不回滚** 事务传播问题* self 前提下,删除outboundService.cancelDelivery 事务注解* @当 outboundService.cancelDelivery 方法没有 @Transactional 注解,但被一个已有事务的方法调用时,它会加入当前事务。** 事务独立问题* self 前提下,一个成功,一个失败,失败会回滚** 分布式事务问题* 配送失败,回滚,出库单,取货单* @生效*/
解决方法
通过self实现自调用
OutboundSellOrderCancelVO result = self.processSingleOrderInTransaction(outCode, form);
类实现自注入接口,在类中注入它自己
public class OutboundSellOrderBizService implements BeanSelfAware {
private OutboundSellOrderBizService self;
@Override public void setSelf(Object proxyBean) {self = (OutboundSellOrderBizService) proxyBean; }
/*** spring 自身注入自身方法 , 解决内部方法调用不走代理的问题.** @author Luo* @date 2021-9-23 10:59:37*/ public interface BeanSelfAware {/*** 注入自身对象.** @param proxyBean 代理bean*/void setSelf(Object proxyBean); }
实现原理
总结:要通过代理对象调用,怎么获取代理对象
工作原理
Spring容器初始化:当Spring容器创建OutboundSellOrderBizService bean时,会创建一个代理对象(如果存在AOP切面)
回调注入:Spring通过BeanSelfAware接口的setSelf方法,将代理对象注入到self字段中
疑问
1,代理对象什么实现创建的
2,具体如何将代理对象注入到self字段中