3.Seate
事务协调者TC
事务管理器TM
资源管理器RM
- AT 首推
- TCC
- SAGA
- XA
AT (auto transation)
无侵入
1.一阶段:
拦截业务SQL
before/after image undo/redo
TCC
缺点:侵入性强,需要自己完成对应的业务控制逻辑
优点:性能强(基本无锁)
修改config.txt
事务分组: 异地机房停电容错机制
service.vgroupMapping.my_test_tx_group=default
(比如 shanghai、guangzhou)对应的client也要去设置
运行
3.1 Seata的使用(DB模式)
1.新建数据库 表结构需要在 github上面 alibaba-seata 下载
导入SQL之后
2.新建两个项目 order stock
1.引入 seata 的依赖
每个服务pom引入
<!--nacos服务注册发现--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--添加openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--添加seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency>
2.添加 undo_Log 表
如果公共一个数据库 只添加一个
DROPTABLEIFEXISTS`undo_log`; CREATETABLE`undo_log` ( `id`int(0) NOTNULLAUTO_INCREMENT, `branch_id`bigint(0) NOTNULL, `xid`varchar(100) CHARACTERSETutf8mb4COLLATEutf8mb4_0900_ai_ciNOTNULL, `context`varchar(128) CHARACTERSETutf8mb4COLLATEutf8mb4_0900_ai_ciNOTNULL, `rollback_info`longblobNOTNULL, `log_status`int(0) NOTNULL, `log_created`datetime(0) NULLDEFAULTNULL, `log_modified`datetime(0) NULLDEFAULTNULL, PRIMARYKEY (`id`) USINGBTREE, UNIQUEINDEX`ux_undo_log`(`id`, `branch_id`) USINGBTREE) ENGINE=InnoDBCHARACTERSET=utf8mb4COLLATE=utf8mb4_0900_ai_ciROW_FORMAT=Dynamic; 配置application.ymlserver: port: 30001spring: application: name: order-seatadatasource: username: rootpassword: rooturl: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=UTCdriverClassName: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcejpa: show-sql: truehibernate: ddl-auto: updateproperties: hibernate: format_sql: trueenable_lazy_load_no_trans: trueopen-in-view: falsecloud: alibaba: seata: tx-service-group: guangzhou#配置事务分组nacos: discovery: serverAddr: 127.0.0.1:8848username: nacospassword: nacosseata: registry: #配置seata的注册中心,告诉seataclient怎么去访问seataserver(TC) type: nacosnacos: serverAddr: 127.0.0.1:8848#SEATAServer所在acos服务地址application: seata-server#SEATAServer所在服务名seata-server如果没有修改可以不配username: nacospassword: nacosgroup: SEATA_GROUP#SEATAServer所在的组默认就是SEATA_GROUP如果没有修改可以不配config: #配置seata的配置中心type: nacosnacos: serverAddr: 127.0.0.1:8848username: nacospassword: nacosgroup: SEATA_GROUPserver: port: 30002spring: application: name: stock-seatadatasource: username: rootpassword: rooturl: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=UTCdriverClassName: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcejpa: show-sql: truehibernate: ddl-auto: updateproperties: hibernate: format_sql: trueenable_lazy_load_no_trans: trueopen-in-view: falsecloud: nacos: discovery: serverAddr: 127.0.0.1:8848username: nacospassword: nacosalibaba: seata: tx-service-group: guangzhou#这里必须和config.txt里面的内容保持一致seata: registry: #配置seata的注册中心,告诉seataclient怎么去访问seataserver(TC) type: nacosnacos: serverAddr: 127.0.0.1:8848#SEATAServer所在nacos服务地址application: seata-server#SEATAServer所在服务名seata-server如果没有修改可以不配username: nacospassword: nacosgroup: SEATA_GROUP#SEATAServer所在的组默认就是SEATA_GROUP如果没有修改可以不配config: #配置seata的配置中心type: nacosnacos: serverAddr: 127.0.0.1:8848username: nacospassword: nacosgroup: SEATA_GROUP
3.业务代码实现
使用的是Spring Data JPA 需要引入 spring data jpa 依赖
/*** @ClassName OrderInfo* @Description* @Author Jackson* @Date 2023/5/4* @Version 1.0**/name="order_info") (publicclassOrderInfoimplementsSerializable { strategy=GenerationType.IDENTITY) (privateIntegerorderId; privateStringgoodId; privateLonggoodNum; privateStringremark; privateStringcustomerId; } CREATETABLE`stock_info` ( `stock_id`intNOTNULL, `count_num`bigintDEFAULTNULL, `good_id`varchar(12) CHARACTERSETutf8mb4COLLATEutf8mb4_0900_ai_ciDEFAULTNULL, PRIMARYKEY (`stock_id`) ) ENGINE=MyISAMDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_0900_ai_ci; 模拟库存不足的情况下抛异常//OrderServiceImplpublicStringsaveOrder(OrderInfoorderInfo) { orderInfoDao.save(orderInfo); Stringresult=stockService.reduct(orderInfo.getGoodId(), orderInfo.getGoodNum()); if ("库存不足".equals(result)) { thrownewBizException("库存不足"); } returnresult; } //OrderController//模拟请求"save") (publicStringsaveOrder() { ThreadLocalRandomthreadLocalRandom=ThreadLocalRandom.current(); longgoodNum=threadLocalRandom.nextLong(1,10); OrderInfoorderInfo=newOrderInfo(0, "shoes001", goodNum, "alibaba", "9527"); try { orderService.saveOrder(orderInfo); } catch (Exceptione) { return"库存不足"; } return"success"; }
4.验证结果
订单表 order_info
库存表
stock_Info
初始库存
最终库存
当库存不足时 服务抛出异常 订单记录回滚 不会出现 订单存在 库存为扣减异常现象
Seata AT模式 工作 rollback
PhaseTwo_Rollbacked 第二阶段回滚