一,分布式事務傳播行為
調用鏈描述
一個普通事務注解的方法,調用一個分布式事務注解方法
分布式事務注解方法:包含一個本地更新,和兩個外部服務更新操作,涉及三個服務
問題
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字段中