公众号merlinsea
- seata框架的核心
- 抽取出了一个TC全局事务协调器,所有的分支事务都要向其汇报
- 当在客户端开启一个分布式事务时,TC会为这个分布式事务生成一个唯一的XID,并在后续的其他事务中传递
- 所有的分支事务都执行完阶段一预提交时要等待TC发出的commit或者rollback命令以后才能继续往下执行。
- seata的架构和核心组成
- TC :Transaction coordinator 全局事务协调器,TC是位于seata的中心服务端,用于统一管理各个微服务节点上的事务状态, 并根据这些分布式事务的状态决定是否提交commit还是回滚rollback。
- TM:Transaction manager 事务管理器,用于开启,提交,回滚事务。TM是位于业务端端代码,当开启一个分布式事务时,会生成 一个用于唯一标识这个分布式事务XID,并且这个XID会在后续调用其他微服务中传递。
- RM:Resource manager 资源管理器,用于各个微服务上的数据库资源的管理,并且可以向TC注册分支事务,接受TC传来的提交或者回滚 命令
- 注释
- 注1: seata架构的RM是以jar包的形式嵌入到业务代码中
- 注2: TM 请求 TC 开启一个全局事务, TC 会生成一个 XID 作为该全局事务的编号XID, XID会在微服务的调用链路中传播,保证将多个微 服务的子事务关联在一起。
- 架构:TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端 (Client/Server架构)
- seata执行分布式事务的流程(如下图所示)
- A服务的TM 向 TC 申请开启(Begin)一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。
- A服务的RM向TC注册分支事务
- A服务执行分支事务(这里是本地事务),对数据库做操作
- A服务开始远程调用B服务,并把XID 在微服务调用链路的上下文中传播【保证了 B服务可以为一识别这个事务】。
- B服务的RM向TC注册分支事务,并将其纳入XID对应的全局事务的管辖
- B服务执行分支事务,向数据库做操作
- 全局事务调用链处理完毕,TM 根据有无异常向 TC 发起针对 XID 的全局提交(Commit)或回滚(Rollback)决议。 TC 调度 XID 下管辖的全部分支事务完成提交(Commit)或回滚(Rollback)请求。
- seata执行模式介绍(AT模式)
- 第一阶段: seata 会拦截“业务 SQL”,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”【回滚的时候使用】, 然后执行“业务 SQL”更新业务数据 在业务数据更新之后,再将其保存成“after image”【提交的时候使用】,最后生成行锁 以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性
- 第二阶段:
- 二阶段提交: 因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将阶段一保存的快照数据和行锁删掉,完成数据清理即可。
- 二阶段回滚: 还原业务数据, 回滚方式便是用“before image”还原业务数据。
- seata框架的使用
- 开启seata服务端程序(TC全局事务协调器)
去seata官网下载并部署到linux服务器上,然后用下面的命令启动seata ./seata-server.sh启动
- 在所有涉及分布式事务的微服务中中引入seata客户端赖
- 注意:需要保证seata客户端的版本和seata服务端端版本一致,否则可能出现maven包冲突(难排查)
<!--alibaba微服务整合分布式事务,这个方式才行--> <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> <version>1.3.0</version> </dependency>
- 编写业务,开启分布式全局事务TM事务管理器 @GlobalTransactional
- 注意事务管理器只需要添加在开启事务的哪个接口api上即可,后续被调用的微服务接口上不需要再添加这个注解,在本案例中是加在user微服务的注册接口上
@Override //全局事务管理器TM @GlobalTransactional public JsonData register(UserRegisterRequest registerRequest) { boolean checkCode = false; //校验验证码 if (StringUtils.isNotBlank(registerRequest.getMail())) { checkCode = notifyService.checkCode(SendCodeEnum.USER_REGISTER, registerRequest.getMail(), registerRequest.getCode()); } if (!checkCode) { return JsonData.buildResult(BizCodeEnum.CODE_ERROR); } UserDO userDO = new UserDO(); BeanUtils.copyProperties(registerRequest, userDO); userDO.setCreateTime(new Date()); userDO.setSlogan("人生需要动态规划,学习需要贪心算法"); //设置密码 生成秘钥 盐 userDO.setSecret("$1$" + CommonUtil.getStringNumRandom(8)); //密码+盐处理 String cryptPwd = Md5Crypt.md5Crypt(registerRequest.getPwd().getBytes(), userDO.getSecret()); userDO.setPwd(cryptPwd); //账号唯一性检查 if (checkUnique(userDO.getMail())) { int rows = userMapper.insert(userDO); log.info("rows:{},注册成功:{}", rows, userDO.toString()); //新用户注册成功,初始化信息,发放福利等 TODO userRegisterInitTask(userDO); //模拟异常 //int b = 1/0; return JsonData.buildSuccess(); } else { return JsonData.buildResult(BizCodeEnum.ACCOUNT_REPEAT); } }