通知短信+运营短信,5秒速达,支持群发助手一键发送🚀高效触达和通知客户 广告
# 分布式事务 ## 说明 Zebra 微服务框架集成了 [Seata](https://github.com/seata/seata) 的 TCC 模式用于支持分布式事务。 虽然 Zebra 微服务框架提供了分布式事务组件,但是仍需开发者实现各种异常处理(如空回滚、幂等、悬挂),实现一个较为完备分布式事务复杂度也还是不低,请确定是否确实要使用分布式事务。 ## 样例 请参考 [https://gitee.com/gszebra/zebra/tree/master/zebra-sample/distributed-transaction](https://gitee.com/gszebra/zebra/tree/master/zebra-sample/distributed-transaction) ## 开发 ### 引入依赖 引入如下依赖 ```markup <dependency> <groupId>com.guosen</groupId> <artifactId>zebra-distributed-transaction</artifactId> <version>${zebra.version}</version> </dependency> ``` ### 配置 #### 配置样例 ```text zebra.distributed.transaction.service.default.grouplist=seataServerIP:seataServerPort ``` #### 配置说明 | 配置项 | 说明 | | :--- | :--- | | zebra.distributed.transaction.service.default.grouplist | Seata Server 服务器地址信息,格式为 IP:PORT | ### 代码编写 #### RM 资源的封装 对于分布式事务用到的各种资源,比如 DB 和远程微服务调用。必须定义一个 LocalTCC 接口给包装起来。 ```java @LocalTCC public interface BusBookingService { @TwoPhaseBusinessAction(name = "BusTccAction" , commitMethod = "commit", rollbackMethod = "rollback") boolean prepare(BusinessActionContext actionContext, @BusinessActionContextParameter(paramName = "busBooking") BusBooking busBooking); /** * Commit boolean. * * @param actionContext the action context * @return the boolean */ boolean commit(BusinessActionContext actionContext); /** * Rollback boolean. * * @param actionContext the action context * @return the boolean */ boolean rollback(BusinessActionContext actionContext); } ``` 如上图,在接口添加如下注解 * @LocalTCC,在接口类上添加 * @TwoPhaseBusinessAction,在 TCC 中的 Try 函数(Java 中 try 是关键字,所以样例中函数名为 prepare)上添加,同时此注解必须设置三个属性。 | 属性名称 | 说明 | | :--- | :--- | | name | TCC bean 名称,必须确保唯一 | | commitMethod | TCC 中 Commit 步骤要调用的此接口的函数名称 | | rollbackMethod | TCC 中 Cancel 步骤要调用的此接口的函数名称 | 同时,commit 和 cancel 对应的函数返回类型必须为 boolean,以便 TCC 框架知道 Commit 或者 Cancel 的结果。 * @BusinessActionContextParameter,如果后续的 commit/rollback 函数要获取到传递给 prepare 函数的参数,则在 prepare 函数的参数上添加此注解。 #### 全局事务的封装 ```java @Autowired private BusBookingService busBookingService; @Autowired private HotelBookingService hotelBookingService; /** * 添加GlobalTransactional注解,以便开启分布式事务 * 必须在实现类上面加,不能在接口上加。 */ @GlobalTransactional @Override public void booking() { LOGGER.info("Begin booking"); String xid = RootContext.getXID(); LOGGER.info("xid is : {}", xid); BusBooking busBooking = new BusBooking(); busBooking.setBusId("bus001"); busBooking.setUserId("user001"); HotelBooking hotelBooking = new HotelBooking(); hotelBooking.setHotelId("hotel001"); hotelBooking.setUserId("user001"); // 调用TCC各个服务的prepare方法 // TCC在prepare执行完毕后,如果全部prepare都成功,则调用对应的commit函数(一直尝试直到所有commit成功) // 如果有部分prepare失败,则调用对应的rollback函数(一直尝试直到所有rollback成功) BusinessActionContext actionContext = new BusinessActionContext(); busBookingService.prepare(actionContext, busBooking); hotelBookingService.prepare(actionContext, hotelBooking); LOGGER.info("Finish booking"); } ``` 如上图,在**实现类**(注意是**类**而非**接口**)的函数添加 @GlobalTransactional。和普通 Spring 一样,使用 @Autowired 注入加了 @LocalTCC 的接口,然后在 @GlobalTransactional 注解的函数里调用它们的 prepare 函数。 TCC 会为加了 @GlobalTransactional 注解的函数的类生成代理,当此函数被调用时,开启分布式事务。 ### TCC 异常处理 请参考文章[分布式事务 Seata TCC 模式深度解析](https://blog.csdn.net/huaishu/article/details/89880971)的第三节“TCC 异常控制”。 > 在有了一套完备的 TCC 接口之后,是不是就真的高枕无忧了呢?答案是否定的。在微服务架构下,很有可能出现网络超时、重发,机器宕机等一系列的异常 Case。一旦遇到这些 Case,就会导致我们的分布式事务执行过程出现异常。根据蚂蚁金服内部多年的使用来看,最常见的主要是这三种异常,分别是空回滚、幂等、悬挂。 > > 因此,TCC 接口里还需要解决这三类异常。实际上,这三类问题可以在 Seata 框架里完成,只不过我们现在的 Seata 框架还不具备,之后我们会把这些异常 Case 的处理移植到 Seata 框架里,业务就无需关注这些异常情况,专注于业务逻辑即可。 > > 虽然业务之后无需关心,但是了解一下其内部实现机制,也能更好的排查问题……