分布式事务Seata1.5.2整合Nacos——TCC模式(三)

分布式事务Seata1.5.2整合Nacos——TCC模式(三)

一、TCC服务搭建

介绍: TCC基于前两篇文章搭建(该文章需要有一定的spring cloud基础)
ServerA: 提供A服务(内部服务,只对ServerC提供服务)
ServerB: 提供B服务(内部服务,只对ServerC提供服务)
ServerC: 提供对外访问服务,通过Fegin远程调用ServerA、ServerB两个服务来实现分布式事务
数据源: 测试时,ServerA、ServerB可以使用同一数据源
二阶段出现异常: 可在seata管理界面查看分布式事务信息

1、ServerA服务搭建

  1. 添加Seata依赖(版本1.5.2通过父工程控制)

    <!--分布式事务 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </dependency>
    
  2. Controller实现

    @ApiOperation(value = "修改")
    @PostMapping("/updateByIdTCC")
    public ResponseResult<Boolean> updateByIdTCC(@RequestBody SysUser sysUser){
        return ResponseMsg.put(sysUserService.updateByIdTCC(sysUser));
    }
    
  3. Service实现

    //这里定义tcc的接口,一定要定义在接口上
    //我们使用springCloud的远程调用,那么这里使用LocalTCC便可
    @LocalTCC
    public interface SysUserService extends IService<SysUser> {
    	/**
         * 定义两阶段提交
         * name = 该tcc的bean名称,全局唯一
         * commitMethod = commit 为二阶段确认方法
         * rollbackMethod = rollback 为二阶段取消方法
         * BusinessActionContextParameter注解 传递参数到二阶段中
         *
         * @param sysUser  -入参
         * @return String
         */
        @TwoPhaseBusinessAction(name = "updateByIdTCC", commitMethod = "commitTCC", rollbackMethod = "rollbackTCC")
        Boolean updateByIdTCC(@BusinessActionContextParameter(paramName = "sysUser")SysUser sysUser);
    
        Boolean commitTCC(BusinessActionContext actionContext);
    
        Boolean rollbackTCC(BusinessActionContext actionContext);
    }
    
  4. ServiceImpl接口实现类

    @Slf4j
    @Service
    public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
    	//用来锁定一阶段资源
    	private final Map<String, SysUser> sysUserMap = new ConcurrentHashMap<>();
        
        @Transactional(rollbackFor = Exception.class)
        @Override
        public Boolean updateByIdTCC(SysUser sysUser) {
            log.info("SpSysUser一阶段---------xid = " + RootContext.getXID() + "提交成功");
            SysUser oldSysUser = findById(sysUser.getId());
            sysUserMap.put(RootContext.getXID(),oldSysUser);
            return true;
        }
    
        @Override
        public Boolean commitTCC(BusinessActionContext actionContext) {
    
            JSONObject jsonObject = (JSONObject) actionContext.getActionContext("sysUser");
            assert jsonObject != null;
            SysUser sysUser = jsonObject.toJavaObject(SysUser.class);
            boolean flag = updateById(sysUser);
            System.out.println("SysUser二阶段提交成功-----xid = " + actionContext.getXid() + "-----" + sysUser);
            //todo 若一阶段资源预留,这里则要提交资源
            return flag;
        }
    
        @Override
        public Boolean rollbackTCC(BusinessActionContext actionContext) {
            //todo 这里写中间件、非关系型数据库的回滚操作
            SysUser sysUser = sysUserMap.get(actionContext.getXid());
            // 进行回滚业务
            updateById(sysUser);
            System.out.println("SpSysUser二阶段提交失败,进行回滚 XID = " + actionContext.getXid() + "--------" + sysUser);
            return true;
        }
    }
    
  5. Fegin接口实现

    @FeignClient(contextId = "remoteSysUserService", value = ServiceNameConstants.NETTY_MYBATIS_PLUS_TEST,
            fallbackFactory = RemoteSysUserFallbackFactory.class)
    public interface RemoteSysUserService {
    	@PostMapping("/sysUser/updateByIdTCC")
        ResponseResult<Boolean> updateByIdTCC(@RequestBody SysUser sysUser);
    }
    

2、ServerB服务搭建

  1. 添加Seata依赖(版本1.5.2通过父工程控制)
    <!--分布式事务 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </dependency>
    
  2. Controller实现
    @ApiModelProperty("根据id修改")
    @PostMapping("/updateSpSysUserById")
    public ResponseResult<Boolean> updateTCCSpSysUserById(@RequestBody SpSysUser spSysUser){
        return ResponseMsg.put(spSysUserService.updateSpSysUserById(spSysUser));
    }
    
  3. Service实现
    //这里定义tcc的接口,一定要定义在接口上
    // 我们使用springCloud的远程调用,那么这里使用LocalTCC便可
    @LocalTCC
    public interface SpSysUserService extends IService<SpSysUser> {
        /**
         * 定义两阶段提交
         * name = 该tcc的bean名称,全局唯一
         * commitMethod = commit 为二阶段确认方法
         * rollbackMethod = rollback 为二阶段取消方法
         * BusinessActionContextParameter注解 传递参数到二阶段中
         *
         * @param spSysUser  -入参
         * @return String
         */
        @TwoPhaseBusinessAction(name = "updateSpSysUserById", commitMethod = "commitTCC", rollbackMethod = "rollbackTCC")
        Boolean updateSpSysUserById(@BusinessActionContextParameter(paramName = "spSysUser")SpSysUser spSysUser);
    
        Boolean commitTCC(BusinessActionContext actionContext);
    
        Boolean rollbackTCC(BusinessActionContext actionContext);
    }
    
  4. ServiceImpl接口实现类
    @Slf4j
    @Service
    public class SpSysUserServiceImpl extends ServiceImpl<SpSysUserMapper, SpSysUser> implements SpSysUserService {
    	//用来锁定一阶段资源
        private final Map<String, SpSysUser> oldSpSysUserMap = new ConcurrentHashMap<>();
        
        @Transactional(rollbackFor = Exception.class)
        @NettySystemPlatform   //不同数据源设置
        @Override
        public Boolean updateSpSysUserById(SpSysUser spSysUser) {
            log.info("SpSysUser一阶段---------xid = " + RootContext.getXID() + "提交成功");
            SpSysUser oldSpSysUser = getById(spSysUser.getId());
            if (oldSpSysUser == null){
                throw new RuntimeException("没有获取到信息");
            }
            oldSpSysUserMap.put(RootContext.getXID(),oldSpSysUser);
            return true;
        }
    
        @Override
        public Boolean commitTCC(BusinessActionContext actionContext) {
            //todo 若一阶段资源预留,这里则要提交资源
            JSONObject jsonObject = (JSONObject) actionContext.getActionContext("spSysUser");
            assert jsonObject != null;
            SpSysUser spSysUser = jsonObject.toJavaObject(SpSysUser.class);
            boolean flag = updateById(spSysUser);
            log.info("SpSysUser二阶段---------xid = " + actionContext.getXid() + "提交成功");
            return flag;
        }
    
        @Override
        public Boolean rollbackTCC(BusinessActionContext actionContext) {
            //todo 这里写中间件、非关系型数据库的回滚操作
            SpSysUser spSysUser = oldSpSysUserMap.get(actionContext.getXid());
            assert spSysUser != null;
            boolean flag = updateById(spSysUser);
            System.out.println("SpSysUser二阶段提交失败,进行回滚 XID = " + actionContext.getXid() + "-------------" + spSysUser);
            return flag;
        }
    }
    
  5. Fegin接口实现
    @FeignClient(contextId = "remoteSpSysUserService", value = ServiceNameConstants.NETTY_SYSTEM_PLATFORM,
        fallbackFactory = RemoteSpSysUserFallbackFactory.class)
    public interface RemoteSpSysUserService {
    
        @PostMapping("/spSysUser/updateSpSysUserById")
        ResponseResult<Boolean> updateSpSysUserById(@RequestBody SpSysUser spSysUser);
    
    }
    

3、ServerC服务搭建

  1. 添加Seata依赖(版本1.5.2通过父工程控制)
    在这里插入代码片
    
  2. Controller实现
    @RestController
    @Api(tags="testSeata 控制器")
    @RequestMapping("/testSeataTCC")
    public class TestSeataTCCController {
    
        private final Logger logger = LoggerFactory.getLogger(TestSeataTCCController.class);
    
        @Autowired
        public TestSeataTCCService testSeataTCCService;
    
        @GetMapping("/testSeataTCC")
        public ResponseResult<Boolean> testSeataTCC(){
            return ResponseMsg.put(testSeataTCCService.testSeataTCC());
        }
    
    }
    
  3. Service实现
    public interface TestSeataTCCService {
        Boolean testSeataTCC();
    }
    
  4. ServiceImpl接口实现类
    @Slf4j
    @Service
    public class TestSeataTCCServiceImpl implements TestSeataTCCService {
    
        @Autowired
        private RemoteSysUserService remoteSysUserService;
    
        @Autowired
        private RemoteSpSysUserService remoteSpSysUserService;
    
    
        @GlobalTransactional
        @Override
        public Boolean testSeataTCC() {
            String xid = RootContext.getXID();
            log.info("------->分布式操作开始");
            BusinessActionContext actionContext = new BusinessActionContext();
            actionContext.setXid(xid);
            
            SysUser sysUser = new SysUser();
            sysUser.setId(1L);
            sysUser.setUsername("李四");
            sysUser.setPassword("123456");
            remoteSysUserService.updateByIdTCC(sysUser);
    
            SpSysUser spSysUser = new SpSysUser();
            spSysUser.setId(1L);
            spSysUser.setName("李四");
            ResponseResult<Boolean> result = remoteSpSysUserService.updateSpSysUserById(spSysUser);
            log.info("------->分布式操作结束");
            throw new RuntimeException("抛出自定义异常,进行服务tcc测试回滚");
            //return result.getData();
        }
    }
    
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只大耗子。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值