首页 > 其他分享 >seata TCC 模式 快速demo笔记

seata TCC 模式 快速demo笔记

时间:2024-12-16 17:01:04浏览次数:6  
标签:10.1 java seata 19 demo jar 6.1 org TCC

之前笔记整理过AT模式:分布式事务seata(AT)与nacos整合-笔记2-CSDN博客

对于TCC模式是类似的。相对于AT更灵活。

1 TCC 模式原理

官方介绍:

Seata TCC 模式 | Apache Seata

这个介绍比较简单,demo可以快速体验下。

实际落地根据自己业务模式来考虑,核心关注点有3个:幂等、允许空回滚、防悬挂控制。

对于用户接入 TCC ,最重要的是考虑如何将自己的业务模型拆成两阶段来实现。

比如之前的扣钱,不能直接扣钱,拆成两阶段,实现成三个方法,并且保证一阶段 Try  成功的话 二阶段 Confirm 一定能成功。

一阶段:

TRY: 冻结部分扣款。

二阶段:

confirm: 真正扣款,把冻结金额清除。

cancel:释放一阶段 Try 冻结的 金额,还原回去。

类似的扣库存也是一个道理。但是对于下单就不好直接操作,需要靠逻辑的状态预下单等方式变通处理。

2 demo 快速验证

demo 没有考虑幂等、允许空回滚、防悬挂控制这些因素。基于之前的工程临时加了个表

CREATE TABLE `storage_freeze` (
  `xid` varchar(250) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '事务id',
  `commodity_code` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '用户id',
  `freeze_storage` int unsigned DEFAULT '0' COMMENT '冻结库存',
  `state` int DEFAULT NULL COMMENT '事务状态,1:try,0:cancel',
  PRIMARY KEY (`xid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=COMPACT;

还可以再原表上加冻结字段来表示。都可以。仅作为示意。

对比 AT改动点:

2.1发起方:

order 下单接口,仅改变Feign的接口url

    //at
	@PostMapping("/storage/reduce-stock")
    Result<?> reduceStock(@RequestBody StorageDTO productReduceStockDTO);
    // tcc
    @PostMapping("/storage/prepare")
    Result<?> prepare(@RequestBody StorageDTO productReduceStockDTO);

服务发起调用方order注解:@GlobalTransactional 不变。

/*
 * Copyright 2013-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.tuling.tlmallorder.service.impl;


import io.seata.core.context.RootContext;
import io.seata.spring.annotation.GlobalTransactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import org.tuling.tlmallcommon.BusinessException;
import org.tuling.tlmallcommon.Result;
import org.tuling.tlmallorder.entity.Order;
import org.tuling.tlmallorder.feign.AccountServiceFeignClient;
import org.tuling.tlmallorder.feign.StorageServiceFeignClient;
import org.tuling.tlmallorder.feign.dto.AccountDTO;
import org.tuling.tlmallorder.feign.dto.StorageDTO;
import org.tuling.tlmallorder.mapper.OrderMapper;
import org.tuling.tlmallorder.service.OrderService;

import java.sql.Timestamp;
import java.util.List;

import static org.tuling.tlmallcommon.ResultEnum.COMMON_FAILED;


@Service
public class OrderServiceImpl implements OrderService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private AccountServiceFeignClient accountService;

    @Autowired
    private StorageServiceFeignClient storageService;

    @Autowired
    RestTemplate restTemplate;

    @Override
    @GlobalTransactional(name="createOrder")
    public Result<?> createOrder(String userId, String commodityCode, Integer count) {

        logger.info("[createOrder] current XID: {}", RootContext.getXID());

        // deduct storage
        StorageDTO storageDTO = new StorageDTO();
        storageDTO.setCommodityCode(commodityCode);
        storageDTO.setCount(count);
        //RestTemplate远程调用
        //String storage_url = "http://localhost:8010/storage/reduce-stock";
        //整合了Nacos+LoadBalaner,可以使用微服务名tlmall-storage代替localhost:8020
        //String storage_url = "http://tlmall-storage/storage/reduce-stock";
        //Integer storageCode = restTemplate.postForObject(storage_url,storageDTO, Result.class).getCode();
        //openFeign远程调用
       // Integer storageCode = storageService.reduceStock(storageDTO).getCode();
        Integer storageCode = storageService.prepare(storageDTO).getCode();
        if (storageCode.equals(COMMON_FAILED.getCode())) {
            throw new BusinessException("stock not enough");
        }

        // deduct balance
        int price = count * 2;
        AccountDTO accountDTO = new AccountDTO();
        accountDTO.setUserId(userId);
        accountDTO.setPrice(price);
        //RestTemplate远程调用
        //String account_url = "http://localhost:8020/account/reduce-balance";
        //整合了Nacos+LoadBalaner,可以使用微服务名tlmall-account代替localhost:8020
        //String account_url = "http://tlmall-account/account/reduce-balance";
        //Integer accountCode = restTemplate.postForObject(account_url, accountDTO, Result.class).getCode();
        //openFeign远程调用
    //    Integer accountCode = accountService.reduceBalance(accountDTO).getCode();
        Integer accountCode = accountService.prepare(accountDTO).getCode();
        if (accountCode.equals(COMMON_FAILED.getCode())) {
            throw new BusinessException("balance not enough");
        }

        // save order
        Order order = new Order();
        order.setUserId(userId);
        order.setCommodityCode(commodityCode);
        order.setCount(count);
        order.setMoney(price);
        order.setCreateTime(new Timestamp(System.currentTimeMillis()));
        order.setUpdateTime(new Timestamp(System.currentTimeMillis()));
        orderMapper.saveOrder(order);
        logger.info("[createOrder] orderId: {}", order.getId());

        return Result.success(order);
    }

    @Override
    public Result<?> getOrderByUserId(String userId) {
        List<Order> list = orderMapper.getOrderByUserId(userId);

        return Result.success(list);
    }

}

2.2 被调用方举例(account服务):

AccountController 提供新的入口
@PostMapping("/prepare")
	public Result<?> prepareReduceBalance(@RequestBody AccountDTO accountDTO) {
		try {
			tccAccountService.prepare(accountDTO.getUserId(), accountDTO.getPrice());
		}
		catch (BusinessException e) {
			return Result.failed(e.getMessage());
		}
		return Result.success("");
	}

服务:

接口:

package org.tuling.tlmallaccount.service;

import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
import org.tuling.tlmallcommon.BusinessException;

@LocalTCC
public interface TCCAccountService {

    /**
     *
     * @param userId
     * @param price
     * @throws BusinessException
     */
    @TwoPhaseBusinessAction(name="prepare",commitMethod = "commit",rollbackMethod = "rollback")
    void  prepare(String userId, Integer price) throws BusinessException;

    /**
     * 二阶段提交
     * @param businessActionContext
     * @return
     */
    public boolean commit(BusinessActionContext businessActionContext);

    /**
     * 二阶段回滚
     * @param businessActionContext
     * @return
     */
    public boolean rollback(BusinessActionContext businessActionContext);
}

对应具体接口实现 

package org.tuling.tlmallaccount.service.impl;

import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.tuling.tlmallaccount.dto.AccountFreeze;
import org.tuling.tlmallaccount.mapper.AccountFreezeMapper;
import org.tuling.tlmallaccount.mapper.AccountMapper;
import org.tuling.tlmallaccount.service.TCCAccountService;
import org.tuling.tlmallcommon.BusinessException;

import java.sql.Timestamp;

@Service
public class TCCAccountServiceImpl implements TCCAccountService {
    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountFreezeMapper freezeMapper;

    @Override
    @Transactional
    public void prepare(@BusinessActionContextParameter(paramName = "userId") String userId,
                        @BusinessActionContextParameter(paramName = "price") Integer price) throws BusinessException {
        String xid = RootContext.getXID();
        logger.info("account [prepareReduce] current XID: "+xid+",price:"+price);

        // 1.幂等处理
//        if (TccActionHandler.hasPrepareResult(xid)) {
//            return ;
//        }
        // 2.防止悬挂,已经执行过回滚了就不能再预留资源
//        if (TccActionHandler.hasRollbackResult(xid) || TccActionHandler.hasCommitResult(xid)) {
//            return ;
//        }

        //减钱
        Timestamp updateTime = new Timestamp(System.currentTimeMillis());
        int updateCount = accountMapper.reduceBalance(userId, price, updateTime);
        if (updateCount == 0) {
            throw new BusinessException("reduce balance failed");
        }
        //记录冻结金额
        AccountFreeze freeze = new AccountFreeze();
        freeze.setUserId(userId);
        freeze.setFreezeMoney(price);
        // 1 表示 try 状态,0 表示 cancel 状态
        freeze.setState(1);
        freeze.setXid(xid);
        int insert =freezeMapper.insert(freeze);
        // 4.更新分支事务Try状态,执行成功
//        TccActionHandler.prepareSuccess(RootContext.getXID());
        logger.info("account [prepare] over,xid={},insert:{}",xid,insert);
    }

    //事务成功提交的方法
    @Override
    @Transactional
    public boolean commit(BusinessActionContext businessActionContext) {
        // 1.幂等处理
//        if (TccActionHandler.hasCommitResult(xid)) {
//            return true;
//        }
        //获取事务id
        String xid = businessActionContext.getXid();
        logger.info("account commit ,xid:{}",xid);
        //根据id删除冻结记录
        int count = freezeMapper.deleteById(xid);
        logger.info("account [commit] over,xid={},delete:{}",xid,count);
        // 3.更新分支事务Try状态,执行成功
//      TccActionHandler.commitSuccess(businessActionContext.getXid());
        return true;
    }

    @Override
    @Transactional
    public boolean rollback(BusinessActionContext businessActionContext) {
        //通过事务id查询冻结记录中的金额
        String xid = businessActionContext.getXid();
        logger.info("account rollback ,xid:{}",xid);

        // 1.幂等处理
//        if (TccActionHandler.hasRollbackResult(xid)) {
//            return true;
//        }
//        // 2.没有预留资源结果,回滚不做任何处理;
//        if (!TccActionHandler.hasPrepareResult(xid)) {
//            // 设置回滚结果,防止空悬挂
//            TccActionHandler.rollbackResult(xid);
//            return true;
//        }
        
        AccountFreeze freeze = freezeMapper.selectById(xid);

        if (freeze == null) {
            logger.info("空回滚,xid:{}",xid);
            return true;
        }
        //如果获取到的冻结记录,状态本身已经是 cancel 状态,则不再进行处理
        if (freeze.getState() == 0) {
            return true;
        }


        //恢复余额
        Timestamp updateTime = new Timestamp(System.currentTimeMillis());
        accountMapper.addBalance(freeze.getUserId(), freeze.getFreezeMoney(),updateTime);
        //将冻结金额清零,状态改为 cancel
        //1 表示 try 状态,0 表示 cancel 状态
        freeze.setFreezeMoney(0);
        freeze.setState(0);
        freezeMapper.updateById(freeze);
        // 设置回滚结果
//        TccActionHandler.rollbackResult(xid);
        return true;
    }
}

这里需要注意的是每个方法都必须声明 @Transactional,接口层要有注解

其中对外暴漏的事服务的入口prepare方法,提交、回滚方法seata框架自己会调用。

类似的库存服务就不贴了。

2.3 验证

调用下单接口,看下成果日志:能看到模式变成TCC,能正常提交

//account日志

2024-12-16T11:33:21.065+08:00  INFO 26540 --- [tlmall-account] [nio-8020-exec-1] io.seata.rm.AbstractResourceManager      : branch register success, xid:192.168.59.96:8091:7566644201535903765, branchId:7566644201535903785, lockKeys:null
2024-12-16T11:33:21.165+08:00  INFO 26540 --- [tlmall-account] [nio-8020-exec-1] o.t.t.s.impl.TCCAccountServiceImpl       : account [prepareReduce] current XID: 192.168.59.96:8091:7566644201535903765,price:2
2024-12-16T11:33:21.480+08:00  INFO 26540 --- [tlmall-account] [nio-8020-exec-1] o.t.t.s.impl.TCCAccountServiceImpl       : account [prepare] over,xid=192.168.59.96:8091:7566644201535903765,insert:1
2024-12-16T11:33:24.110+08:00  INFO 26540 --- [tlmall-account] [h_RMROLE_1_1_16] i.s.c.r.p.c.RmBranchCommitProcessor      : rm client handle branch commit process:BranchCommitRequest{xid='192.168.59.96:8091:7566644201535903765', branchId=7566644201535903785, branchType=TCC, resourceId='prepare', applicationData='{"actionContext":{"action-start-time":1734320000765,"useTCCFence":false,"sys::prepare":"prepare","price":2,"sys::rollback":"rollback","sys::commit":"commit","host-name":"192.168.59.96","userId":"fox","actionName":"prepare"}}'}
2024-12-16T11:33:24.116+08:00  INFO 26540 --- [tlmall-account] [h_RMROLE_1_1_16] io.seata.rm.AbstractRMHandler            : Branch committing: 192.168.59.96:8091:7566644201535903765 7566644201535903785 prepare {"actionContext":{"action-start-time":1734320000765,"useTCCFence":false,"sys::prepare":"prepare","price":2,"sys::rollback":"rollback","sys::commit":"commit","host-name":"192.168.59.96","userId":"fox","actionName":"prepare"}}
2024-12-16T11:33:24.223+08:00  INFO 26540 --- [tlmall-account] [h_RMROLE_1_1_16] o.t.t.s.impl.TCCAccountServiceImpl       : account commit ,xid:192.168.59.96:8091:7566644201535903765
2024-12-16T11:33:24.294+08:00  INFO 26540 --- [tlmall-account] [h_RMROLE_1_1_16] o.t.t.s.impl.TCCAccountServiceImpl       : account [commit] over,xid=192.168.59.96:8091:7566644201535903765,delete:1
2024-12-16T11:33:24.359+08:00  INFO 26540 --- [tlmall-account] [h_RMROLE_1_1_16] io.seata.rm.AbstractResourceManager      : TCC resource commit result : true, xid: 192.168.59.96:8091:7566644201535903765, branchId: 7566644201535903785, resourceId: prepare
2024-12-16T11:33:24.360+08:00  INFO 26540 --- [tlmall-account] [h_RMROLE_1_1_16] io.seata.rm.AbstractRMHandler            : Branch commit result: PhaseTwo_Committed

扣库存日志


2024-12-16T11:33:18.319+08:00  INFO 21792 --- [tlmall-storage] [nio-8010-exec-1] io.seata.rm.AbstractResourceManager      : branch register success, xid:192.168.59.96:8091:7566644201535903765, branchId:7566644201535903776, lockKeys:null
2024-12-16T11:33:19.082+08:00  INFO 21792 --- [tlmall-storage] [nio-8010-exec-1] o.t.t.service.impl.StorageServiceImpl    : storage [prepare] current XID: 192.168.59.96:8091:7566644201535903765,orderCount:1
2024-12-16T11:33:19.233+08:00  INFO 21792 --- [tlmall-storage] [nio-8010-exec-1] o.t.t.service.impl.StorageServiceImpl    : storage [prepare] over,xid=192.168.59.96:8091:7566644201535903765,insert:1
2024-12-16T11:33:23.850+08:00  INFO 21792 --- [tlmall-storage] [h_RMROLE_1_6_16] i.s.c.r.p.c.RmBranchCommitProcessor      : rm client handle branch commit process:BranchCommitRequest{xid='192.168.59.96:8091:7566644201535903765', branchId=7566644201535903776, branchType=TCC, resourceId='prepare', applicationData='{"actionContext":{"action-start-time":1734319998226,"useTCCFence":false,"sys::prepare":"prepare","commodityCode":"1","orderCount":1,"sys::rollback":"rollback","sys::commit":"commit","host-name":"192.168.59.96","actionName":"prepare"}}'}
2024-12-16T11:33:23.850+08:00  INFO 21792 --- [tlmall-storage] [h_RMROLE_1_6_16] io.seata.rm.AbstractRMHandler            : Branch committing: 192.168.59.96:8091:7566644201535903765 7566644201535903776 prepare {"actionContext":{"action-start-time":1734319998226,"useTCCFence":false,"sys::prepare":"prepare","commodityCode":"1","orderCount":1,"sys::rollback":"rollback","sys::commit":"commit","host-name":"192.168.59.96","actionName":"prepare"}}
2024-12-16T11:33:23.918+08:00  INFO 21792 --- [tlmall-storage] [h_RMROLE_1_6_16] o.t.t.service.impl.StorageServiceImpl    : storage commit ,xid:192.168.59.96:8091:7566644201535903765
2024-12-16T11:33:24.057+08:00  INFO 21792 --- [tlmall-storage] [h_RMROLE_1_6_16] io.seata.rm.AbstractResourceManager      : TCC resource commit result : true, xid: 192.168.59.96:8091:7566644201535903765, branchId: 7566644201535903776, resourceId: prepare
2024-12-16T11:33:24.057+08:00  INFO 21792 --- [tlmall-storage] [h_RMROLE_1_6_16] io.seata.rm.AbstractRMHandler            : Branch commit result: PhaseTwo_Committed

模拟当库存够但是余额不足的情况引发的回滚

2024-12-16T11:43:31.700+08:00  INFO 26540 --- [tlmall-account] [nio-8020-exec-2] io.seata.rm.AbstractResourceManager      : branch register success, xid:192.168.59.96:8091:7566644201535903800, branchId:7566644201535903825, lockKeys:null
2024-12-16T11:43:31.773+08:00  INFO 26540 --- [tlmall-account] [nio-8020-exec-2] o.t.t.s.impl.TCCAccountServiceImpl       : account [prepareReduce] current XID: 192.168.59.96:8091:7566644201535903800,price:2
2024-12-16T11:43:32.021+08:00 ERROR 26540 --- [tlmall-account] [nio-8020-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.RuntimeException: try to proceed invocation error] with root cause

org.tuling.tlmallcommon.BusinessException: reduce balance failed
	at org.tuling.tlmallaccount.service.impl.TCCAccountServiceImpl.prepare(TCCAccountServiceImpl.java:40) ~[classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:351) ~[spring-aop-6.1.5.jar:6.1.5]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.1.5.jar:6.1.5]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.1.5.jar:6.1.5]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:765) ~[spring-aop-6.1.5.jar:6.1.5]
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-6.1.5.jar:6.1.5]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:392) ~[spring-tx-6.1.5.jar:6.1.5]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.1.5.jar:6.1.5]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.5.jar:6.1.5]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:765) ~[spring-aop-6.1.5.jar:6.1.5]
	at io.seata.spring.annotation.AdapterInvocationWrapper.proceed(AdapterInvocationWrapper.java:57) ~[seata-all-2.0.0.jar:2.0.0]
	at io.seata.integration.tx.api.interceptor.ActionInterceptorHandler.proceed(ActionInterceptorHandler.java:104) ~[seata-all-2.0.0.jar:2.0.0]
	at io.seata.rm.tcc.interceptor.TccActionInterceptorHandler.doInvoke(TccActionInterceptorHandler.java:97) ~[seata-all-2.0.0.jar:2.0.0]
	at io.seata.integration.tx.api.interceptor.handler.AbstractProxyInvocationHandler.invoke(AbstractProxyInvocationHandler.java:35) ~[seata-all-2.0.0.jar:2.0.0]
	at io.seata.spring.annotation.AdapterSpringSeataInterceptor.invoke(AdapterSpringSeataInterceptor.java:45) ~[seata-all-2.0.0.jar:2.0.0]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.5.jar:6.1.5]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:765) ~[spring-aop-6.1.5.jar:6.1.5]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:717) ~[spring-aop-6.1.5.jar:6.1.5]
	at org.tuling.tlmallaccount.service.impl.TCCAccountServiceImpl$$SpringCGLIB$$0.prepare(<generated>) ~[classes/:na]
	at org.tuling.tlmallaccount.controller.AccountController.prepareReduceBalance(AccountController.java:60) ~[classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255) ~[spring-web-6.1.5.jar:6.1.5]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188) ~[spring-web-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:925) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:830) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) ~[tomcat-embed-core-10.1.19.jar:6.0]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.19.jar:6.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.5.jar:6.1.5]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.5.jar:6.1.5]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109) ~[spring-web-6.1.5.jar:6.1.5]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.5.jar:6.1.5]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.StandardHostValve.$sw$original$invoke$005sj03(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.StandardHostValve.$sw$original$invoke$005sj03$accessor$$sw$p8ebm33(StandardHostValve.java) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.StandardHostValve$$sw$auxiliary$8iqdhg0.call(Unknown Source) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:86) ~[skywalking-agent.jar:9.3.0]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]

2024-12-16T11:43:33.440+08:00  INFO 26540 --- [tlmall-account] [h_RMROLE_1_2_16] i.s.c.r.p.c.RmBranchRollbackProcessor    : rm handle branch rollback process:BranchRollbackRequest{xid='192.168.59.96:8091:7566644201535903800', branchId=7566644201535903825, branchType=TCC, resourceId='prepare', applicationData='{"actionContext":{"action-start-time":1734320611609,"useTCCFence":false,"sys::prepare":"prepare","price":2,"sys::rollback":"rollback","sys::commit":"commit","host-name":"192.168.59.96","userId":"fox","actionName":"prepare"}}'}
2024-12-16T11:43:33.446+08:00  INFO 26540 --- [tlmall-account] [h_RMROLE_1_2_16] io.seata.rm.AbstractRMHandler            : Branch Rollbacking: 192.168.59.96:8091:7566644201535903800 7566644201535903825 prepare
2024-12-16T11:43:33.527+08:00  INFO 26540 --- [tlmall-account] [h_RMROLE_1_2_16] o.t.t.s.impl.TCCAccountServiceImpl       : account rollback ,xid:192.168.59.96:8091:7566644201535903800
account rollback ,xid:192.168.59.96:8091:7566644201535903800
2024-12-16T11:43:33.596+08:00  INFO 26540 --- [tlmall-account] [h_RMROLE_1_2_16] o.t.t.s.impl.TCCAccountServiceImpl       : 空回滚,xid:192.168.59.96:8091:7566644201535903800
2024-12-16T11:43:33.666+08:00  INFO 26540 --- [tlmall-account] [h_RMROLE_1_2_16] io.seata.rm.AbstractResourceManager      : TCC resource rollback result : true, xid: 192.168.59.96:8091:7566644201535903800, branchId: 7566644201535903825, resourceId: prepare
2024-12-16T11:43:33.666+08:00  INFO 26540 --- [tlmall-account] [h_RMROLE_1_2_16] io.seata.rm.AbstractRMHandler            : Branch Rollbacked result: PhaseTwo_Rollbacked
2024-12-16T11:43:26.426+08:00  INFO 21792 --- [tlmall-storage] [nio-8010-exec-2] io.seata.rm.AbstractResourceManager      : branch register success, xid:192.168.59.96:8091:7566644201535903800, branchId:7566644201535903809, lockKeys:null
2024-12-16T11:43:27.465+08:00  INFO 21792 --- [tlmall-storage] [nio-8010-exec-2] o.t.t.service.impl.StorageServiceImpl    : storage [prepare] current XID: 192.168.59.96:8091:7566644201535903800,orderCount:1
2024-12-16T11:43:27.634+08:00  INFO 21792 --- [tlmall-storage] [nio-8010-exec-2] o.t.t.service.impl.StorageServiceImpl    : storage [prepare] over,xid=192.168.59.96:8091:7566644201535903800,insert:1
2024-12-16T11:43:32.525+08:00  INFO 21792 --- [tlmall-storage] [h_RMROLE_1_7_16] i.s.c.r.p.c.RmBranchRollbackProcessor    : rm handle branch rollback process:BranchRollbackRequest{xid='192.168.59.96:8091:7566644201535903800', branchId=7566644201535903809, branchType=TCC, resourceId='prepare', applicationData='{"actionContext":{"action-start-time":1734320606329,"useTCCFence":false,"sys::prepare":"prepare","commodityCode":"1","orderCount":1,"sys::rollback":"rollback","sys::commit":"commit","host-name":"192.168.59.96","actionName":"prepare"}}'}
2024-12-16T11:43:32.584+08:00  INFO 21792 --- [tlmall-storage] [h_RMROLE_1_7_16] io.seata.rm.AbstractRMHandler            : Branch Rollbacking: 192.168.59.96:8091:7566644201535903800 7566644201535903809 prepare
2024-12-16T11:43:32.659+08:00  INFO 21792 --- [tlmall-storage] [h_RMROLE_1_7_16] o.t.t.service.impl.StorageServiceImpl    : storage rollback ,xid:192.168.59.96:8091:7566644201535903800
2024-12-16T11:43:33.377+08:00  INFO 21792 --- [tlmall-storage] [h_RMROLE_1_7_16] io.seata.rm.AbstractResourceManager      : TCC resource rollback result : true, xid: 192.168.59.96:8091:7566644201535903800, branchId: 7566644201535903809, resourceId: prepare
2024-12-16T11:43:33.378+08:00  INFO 21792 --- [tlmall-storage] [h_RMROLE_1_7_16] io.seata.rm.AbstractRMHandler            : Branch Rollbacked result: PhaseTwo_Rollbacked

看到是触发回滚了。可以再结合数据表数据来验证回滚补偿

3总结

AT 模式是无侵入的分布式事务解决方案,适用于不希望对业务进行改造的场景。

TCC 模式对业务代码有一定的侵入性,但是 TCC 模式无 AT 模式的全局行锁,其分布式事务模型直接作用于服务层,不依赖底层数据库,可以灵活选择业务资源的锁定粒度,TCC 性能会比 AT 模式高很多。最重要的事情就是考虑如何将业务模型拆成 2 阶段,实现成 TCC 的 3 个方法,并且保证 Try 成功 Confirm 一定能成功。适用于核心系统等对性能有很高要求的场景。

没有最好的,只有适合的。

标签:10.1,java,seata,19,demo,jar,6.1,org,TCC
From: https://blog.csdn.net/bohu83/article/details/144502847

相关文章

  • 【人工智能】教你如何利用CodeMoss的OpenAI API调用GPT4大语言模型(最全教程)
    文章目录OpenAIAPIKey的使用场景步骤1:打开[CodeMoss](https://pc.aihao123.cn/index.html#/page/login?invite=1141439&fromChannel=1_Moss1213)工具步骤2:进入API管理界面步骤3:生成新的OpenAIAPI使用OpenAIAPI的实战教程1.可以调用的模型2.Python示例代码(基础)3.Pytho......
  • 商城项目基于消息队列的TCC分布式事务控制改造-----商城项目
    packagecom.alatus.mall.ware.service.impl;importcom.alatus.common.to.mq.StockDetailTo;importcom.alatus.common.to.mq.StockLockedTo;importcom.alatus.common.utils.R;importcom.alatus.common.exception.NoStockException;importcom.alatus.mall.ware.conf......
  • JavaFX 开发摄像头捕捉手势相关技术和简单的demo
    以下是使用JavaFX开发摄像头捕捉手势相关的技术介绍以及一个简单的示例代码(demo),可以实现基本的从摄像头获取图像并尝试进行简单手势相关检测的功能(这里的手势检测只是简单示意,实际完整的手势识别还需要更复杂处理,比如机器学习等算法来准确分类手势)。一、相关技术介绍JavaF......
  • 【Python+Flask+OpenAI】利用OpenAI API Key实现GPT4-智能AI对话接口demo - 从0到1手
    文章目录前言环境准备安装必要的库生成OpenAIAPI代码实现详解导入必要的模块创建Flask应用实例配置OpenAIAPI完整代码如下(demo源码)代码解析利用Postman调用接口了解更多AI内容结尾前言Flask作为一个轻量级的PythonWeb框架,凭借其简洁易用的特点,成为构建Web应用......
  • Chromium源码分析五:写一个利用ipc+protobuf通信的demo
    在chromium的进程之间使用ipc+protobuffer的方式通信,这样既能保证对模块松耦合,又可以保证独立运行时不会相互被影响。主要用于以下两个方面:browser进程和系统适配层之间的通信;(包括:音视频播放器或者走行规制相关的接口等)browser进程和render进程之间的通信。只看代码没有什......
  • Dockerfile构建demo
    Dockerfile模板如下:#基础镜像FROMopenjdk:8-jdk-slim#作者信息LABELmaintainer="xyqq"#设置工作目录WORKDIR/app#复制应用程序JAR文件到工作目录COPY*.jarapp.jar#暴露应用端口EXPOSE8080#设置运行环境变量ENVJAVA_OPTS=""#启动容器时执......
  • OSG开发笔记(三十九):OSG中模型的透明度实现、球体透明度Demo
    前言  在OSG中,对于一些效果未被选中或者包含等业务,需要半透明效果来实现。  本篇描述OSG的半透明实现方式。 Demo   透明功能概述  透明效果在三维场景中扮演着重要角色,它能够模拟玻璃、水体、烟雾等自然现象,增加场景的层次感和真实感。然而,透明效......
  • 分布式事务seata详解
    1、seata官方文档官方地址:ApacheSeataseata提供的官方文档,有中文和英文,可以随意切换关于seata的介绍,这里不做赘述,详细可以参考官方文档。Seata为用户提供了AT、TCC、SAGA和XA事务模式,我们这里介绍的为AT事务模式2、安装seataseata官网下载地址:Seata-Server版本历......
  • WhaleStudio Demo:如何从Aurora CDC 实时数据同步到Redshift详细演示
    视频演示:https://weixin.qq.com/sph/AQ0oGKk12今天我将向大家详细介绍如何使用WhaleStudio将数据从AuroraCDC(ChangeDataCapture)实时同步至Redshift的功能。这是一个强大的数据同步功能,它支持多种数据源,包括文件、非结构化数据以及多样化的接口。数据支持范围首先,Whale......
  • 二、locust --locust_demo
    #*_*coding:utf-8*_*#@Author:zyb#HttpUser:保持会话。FastHttpUser:高性能fromlocustimportTaskSet,task,HttpUser,FastHttpUser,between,constant_throughput,constant_pacing,constant#argument_parser这个可以修改执行参数的fromgevent._semaphoreimpo......