分布式事务Seata1.5.2整合Nacos——TCC模式(三)
一、TCC服务搭建
介绍: TCC基于前两篇文章搭建(该文章需要有一定的spring cloud基础)
ServerA: 提供A服务(内部服务,只对ServerC提供服务)
ServerB: 提供B服务(内部服务,只对ServerC提供服务)
ServerC: 提供对外访问服务,通过Fegin远程调用ServerA、ServerB两个服务来实现分布式事务
数据源: 测试时,ServerA、ServerB可以使用同一数据源
二阶段出现异常: 可在seata管理界面查看分布式事务信息
1、ServerA服务搭建
-
添加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>
-
Controller实现
@ApiOperation(value = "修改") @PostMapping("/updateByIdTCC") public ResponseResult<Boolean> updateByIdTCC(@RequestBody SysUser sysUser){ return ResponseMsg.put(sysUserService.updateByIdTCC(sysUser)); }
-
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); }
-
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; } }
-
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服务搭建
- 添加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>
- Controller实现
@ApiModelProperty("根据id修改") @PostMapping("/updateSpSysUserById") public ResponseResult<Boolean> updateTCCSpSysUserById(@RequestBody SpSysUser spSysUser){ return ResponseMsg.put(spSysUserService.updateSpSysUserById(spSysUser)); }
- 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); }
- 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; } }
- 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服务搭建
- 添加Seata依赖(版本1.5.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()); } }
- Service实现
public interface TestSeataTCCService { Boolean testSeataTCC(); }
- 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(); } }