大型网站建设定制,农产品网站开发背景,无锡网站排名优化公司哪家好,新闻媒体发布平台Seata 是一款开源的分布式事务解决方案#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式#xff0c;为用户打造一站式的分布式解决方案
Seata 官网#xff1a;https://seata.io/zh-cn/
Spring Cloud Alibaba 官…Seata 是一款开源的分布式事务解决方案致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式为用户打造一站式的分布式解决方案
Seata 官网https://seata.io/zh-cn/
Spring Cloud Alibaba 官网https://sca.aliyun.com/zh-cn/
版本说明 SpringBoot 版本 2.6.5 SpringCloud 版本 2021.0.1 SpringCloudAlibaba 版本 2021.0.1.0 本文详细说明 数据库服务器版本 mysql 8.0.25 mybatis plus 版本 3.5.1 nacos 版本 1.4.2 seata 客户端版本 1.4.2 seata 服务端版本 1.7.1 本文讲解的是 seata 的 SAGA 事物模型在开始阅读下面内容之前建议先阅读笔者的这篇文章《Spring Cloud Alibaba Seata 实现分布式事物》这篇文章中实现的是 seata 的 AT 事物且笔者的本篇文章《Spring Cloud Alibaba Seata 实现 SAGA 事物》是在《Spring Cloud Alibaba Seata 实现分布式事物》基础上写的很多内容需要先了解涉及seata 和nacos的重复内容笔者在本篇文章中不在赘述因此建议读者先看《Spring Cloud Alibaba Seata 实现分布式事物》之后再学习本篇文章。当然如果你对 seata 的搭建已经非常熟悉那么可以直接开始下面阅读 Saga模式是SEATA提供的长事务解决方案在Saga模式中业务流程中每个参与者都提交本地事务当出现某一个参与者失败则补偿前面已经成功的参与者一阶段正向服务和二阶段补偿服务都由业务开发实现
Saga 文档https://seata.io/zh-cn/docs/user/saga 目录
1、创建项目
1.1、新建 maven 聚合项目 cloud-learn
1.2、创建 account 服务
1.3、创建 order 服务
2、添加配置
2.1、客户端配置
2.2、服务端配置
3、数据库建表
3.1、seata 服务端建表
3.2、seata 客户端建表
3.3、Saga 状态机建表
4、Saga 状态机 json 文件说明
5、运行测试
6、项目代码 1、创建项目
1.1、新建 maven 聚合项目 cloud-learn
最外层父工程 cloud-learn 的 pom.xml
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.wsjzzcbq/groupIdartifactIdcloud-learn/artifactIdversion1.0-SNAPSHOT/versionmodulesmodulegateway-learn/modulemoduleconsumer-learn/modulemodulesentinel-learn/modulemoduleseata-at-account-learn/modulemoduleseata-at-order-learn/modulemoduleseata-tcc-order-learn/modulemoduleseata-tcc-account-learn/modulemoduleseata-saga-account-learn/modulemoduleseata-saga-order-learn/module/modulespackagingpom/packagingrepositoriesrepositoryidnaxus-aliyun/idnamenaxus-aliyun/nameurlhttps://maven.aliyun.com/repository/public/urlreleasesenabledtrue/enabled/releasessnapshotsenabledfalse/enabled/snapshots/repository/repositoriesparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.6.5/versionrelativePath//parentpropertiesspring-cloud.version2021.0.1/spring-cloud.versionspring-cloud-alibaba.version2021.0.1.0/spring-cloud-alibaba.versionalibaba-nacos-discovery.veriosn2021.1/alibaba-nacos-discovery.veriosnalibaba-nacos-config.version2021.1/alibaba-nacos-config.versionspring-cloud-starter-bootstrap.version3.1.1/spring-cloud-starter-bootstrap.versiondruid.version1.1.17/druid.versionmysql.version8.0.11/mysql.versionmybatis-plus.version3.5.1/mybatis-plus.version/propertiesdependencyManagementdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversion${spring-cloud.version}/versiontypepom/typescopeimport/scope/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-alibaba-dependencies/artifactIdversion${spring-cloud-alibaba.version}/versiontypepom/typescopeimport/scope/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactIdversion${alibaba-nacos-discovery.veriosn}/version/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-config/artifactIdversion${alibaba-nacos-config.version}/version/dependency!--spring-cloud-dependencies 2020.0.0 版本不在默认加载bootstrap文件如果需要加载bootstrap文件需要手动添加依赖--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-bootstrap/artifactIdversion${spring-cloud-starter-bootstrap.version}/version/dependencydependencygroupIdcom.alibaba.fastjson2/groupIdartifactIdfastjson2/artifactIdversion2.0.40/version/dependency/dependencies/dependencyManagementdependenciesdependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/dependency/dependencies/project
下面会创建2个服务 account 和 order模拟用户下订单后扣减账户金额服务间使用 feign 调用因为 account 和 order 服务使用不同的数据库因此产生分布式事物使用 seata 解决 1.2、创建 account 服务
创建子工程 seata-saga-account-learn
seata-saga-account-learn pom 文件
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdparentartifactIdcloud-learn/artifactIdgroupIdcom.wsjzzcbq/groupIdversion1.0-SNAPSHOT/version/parentmodelVersion4.0.0/modelVersionartifactIdseata-saga-account-learn/artifactIddependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-config/artifactId/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactId/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactIdversion${druid.version}/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion${mysql.version}/version/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion${mybatis-plus.version}/version/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build
/project
启动类 SeataSAGAAccountApplication
package com.wsjzzcbq;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** SeataSAGAAccountApplication** author wsjz* date 2023/10/24*/
MapperScan(value {com.wsjzzcbq.mapper})
SpringBootApplication
public class SeataSAGAAccountApplication {public static void main(String[] args) {SpringApplication.run(SeataSAGAAccountApplication.class, args);}
}实体类 Account
package com.wsjzzcbq.bean;import lombok.Data;/*** Account** author wsjz* date 2022/07/07*/
Data
public class Account {private Integer id;private String userId;private Integer money;
}AccountMapper
package com.wsjzzcbq.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wsjzzcbq.bean.Account;/*** AccountMapper** author wsjz* date 2023/10/13*/
public interface AccountMapper extends BaseMapperAccount {
}AccountService
package com.wsjzzcbq.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.wsjzzcbq.bean.Account;/*** AccountService** author wsjz* date 2023/10/23*/
public interface AccountService extends IServiceAccount {boolean deductAccount(String userId, int money, boolean rollback);boolean compensateDeductAccount(String userId, int money);
}AccountServiceImpl
package com.wsjzzcbq.service.impl;import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wsjzzcbq.bean.Account;
import com.wsjzzcbq.mapper.AccountMapper;
import com.wsjzzcbq.service.AccountService;
import org.springframework.stereotype.Service;/*** AccountServiceImpl** author wsjz* date 2023/10/23*/
Service
public class AccountServiceImpl extends ServiceImplAccountMapper, Account implements AccountService {Overridepublic boolean deductAccount(String userId, int money, boolean rollback) {System.out.println(扣减);UpdateWrapperAccount up new UpdateWrapper();String sql money money - money;up.setSql(sql);up.eq(user_id, userId);this.update(up);if (rollback) {int a 1/0;}return true;}Overridepublic boolean compensateDeductAccount(String userId, int money) {System.out.println(补偿);UpdateWrapperAccount up new UpdateWrapper();String sql money money money;up.setSql(sql);up.eq(user_id, userId);this.update(up);return true;}
}AccountController
package com.wsjzzcbq.controller;import com.wsjzzcbq.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** AccountController** author wsjz* date 2023/10/23*/
RequestMapping(/account)
RestController
public class AccountController {Autowiredprivate AccountService accountService;GetMapping(/deduct)public boolean deductAccount(String userId, int money, boolean rollback) {return accountService.deductAccount(userId, money, rollback);}GetMapping(/compensate/deduct)public boolean compensateDeductAccount(String userId, int money) {return accountService.compensateDeductAccount(userId, money);}
}application.yml 文件
server:port: 9001
spring:main:allow-circular-references: trueapplication:name: seata-saga-account-learndatasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.3.232:3306/pmc-account?useUnicodetruecharacterEncodingutf8autoReconnecttrueuseSSLfalseserverTimezoneAsia/Shanghaiusername: rootpassword: 123456cloud:nacos:username: nacospassword: nacosserver-addr: 192.168.2.140discovery:namespace: public
# server-addr: 192.168.2.140
# config:
# server-addr:seata:config:type: nacosnacos:server-addr: ${spring.cloud.nacos.server-addr}username: ${spring.cloud.nacos.username}password: ${spring.cloud.nacos.password}group: SEATA_GROUPdata-id: seata-saga.propertiesregistry:type: nacosnacos:application: seata-servercluster: defaultserver-addr: ${spring.cloud.nacos.server-addr}username: ${spring.cloud.nacos.username}password: ${spring.cloud.nacos.password}group: SEATA_GROUPenable-auto-data-source-proxy: falseclient:rm:report-success-enable: true
# 事物分组如果不配置默认是spring.application.name -seata-service-group
# tx-service-group:logging:level:com.wsjzzcbq.mapper: debug配置参数说明可以看《Spring Cloud Alibaba Seata 实现分布式事物》这里不再赘述 1.3、创建 order 服务
创建子工程 seata-saga-order-learn 项目
seata-saga-order-learn pom 文件
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdparentartifactIdcloud-learn/artifactIdgroupIdcom.wsjzzcbq/groupIdversion1.0-SNAPSHOT/version/parentmodelVersion4.0.0/modelVersionartifactIdseata-saga-order-learn/artifactIddependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-config/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-loadbalancer/artifactId/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactId/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactIdversion${druid.version}/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion${mysql.version}/version/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion${mybatis-plus.version}/version/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project
启动类 SeataSAGAOrderApplication
package com.wsjzzcbq;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;/*** SeataSAGAOrderApplication** author wsjz* date 2023/10/24*/
MapperScan(value {com.wsjzzcbq.mapper})
EnableFeignClients
SpringBootApplication
public class SeataSAGAOrderApplication {public static void main(String[] args) {SpringApplication.run(SeataSAGAOrderApplication.class, args);}
}订单实体类 Order
package com.wsjzzcbq.bean;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;/*** Order** author wsjz* date 2022/07/07*/
TableName(order_tbl)
Data
public class Order {TableIdprivate Integer id;private String userId;private String code;private Integer count;private Integer money;
}OrderMapper
package com.wsjzzcbq.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wsjzzcbq.bean.Order;/*** OrderMapper** author wsjz* date 2022/07/07*/
public interface OrderMapper extends BaseMapperOrder {
}AccountFeign
package com.wsjzzcbq.feign;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;/*** AccountFeign** author wsjz* date 2023/10/13*/
FeignClient(value seata-saga-account-learn)
public interface AccountFeign {GetMapping(/account/deduct)boolean deductAccount(RequestParam(userId) String userId, RequestParam(money) int money, RequestParam(rollback) boolean rollback);GetMapping(/account/compensate/deduct)boolean compensateDeductAccount(RequestParam(userId) String userId, RequestParam(money) int money);
}OrderService
package com.wsjzzcbq.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.wsjzzcbq.bean.Order;/*** OrderService** author wsjz* date 2023/10/23*/
public interface OrderService extends IServiceOrder {boolean create(String orderCode, String userId, int money, int count, boolean rollback);boolean compensateCreate(String orderCode, String userId, int money);
}OrderServiceImpl
package com.wsjzzcbq.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wsjzzcbq.bean.Order;
import com.wsjzzcbq.mapper.OrderMapper;
import com.wsjzzcbq.service.OrderService;
import org.springframework.stereotype.Service;/*** OrderServiceImpl** author wsjz* date 2023/10/23*/
Service(orderService)
public class OrderServiceImpl extends ServiceImplOrderMapper, Order implements OrderService {Overridepublic boolean create(String orderCode, String userId, int money, int count, boolean rollback) {System.out.println(order下单);Order order new Order();order.setCode(orderCode);order.setUserId(userId);order.setMoney(money);order.setCount(count);this.save(order);// if (rollback) {
// int a 1/0;
// }return true;}Overridepublic boolean compensateCreate(String orderCode, String userId, int money) {System.out.println(order下单补偿);QueryWrapperOrder queryWrapper new QueryWrapper();queryWrapper.eq(code, orderCode);this.remove(queryWrapper);return true;}
}saga基于状态机调用各个节点这里 seata-saga-order-learn 既是saga事物中的一个节点又是最外层的调用方笔者为了节省代码将调用方和order事物节点放在一起了
saga 事物 AccountService 节点调用
package com.wsjzzcbq.saga;/*** AccountService** author wsjz* date 2023/10/23*/
public interface AccountService {boolean deductAccount(String userId, int money, boolean rollback);boolean compensateDeductAccount(String userId, int money);
}saga 事物 AccountService 节点实现类 AccountServiceImpl
package com.wsjzzcbq.saga.impl;import com.wsjzzcbq.feign.AccountFeign;
import com.wsjzzcbq.saga.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** AccountServiceImpl** author wsjz* date 2023/10/23*/
Service(accountService)
public class AccountServiceImpl implements AccountService {Autowiredprivate AccountFeign accountFeign;Overridepublic boolean deductAccount(String userId, int money, boolean rollback) {System.out.println(userId: userId : money money);try {return accountFeign.deductAccount(userId, money, rollback);} catch (Exception e) {throw new RuntimeException(e.getMessage());}}Overridepublic boolean compensateDeductAccount(String userId, int money) {System.out.println(userId: userId : money money);try {return accountFeign.compensateDeductAccount(userId, money);} catch (Exception e) {throw new RuntimeException(e.getMessage());}}
}saga 状态机配置类 StateMachineEngineConfig
package com.wsjzzcbq.config;import io.seata.saga.engine.config.DbStateMachineConfig;
import io.seata.saga.engine.impl.ProcessCtrlStateMachineEngine;
import io.seata.saga.rm.StateMachineEngineHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import javax.sql.DataSource;
import java.util.concurrent.ThreadPoolExecutor;/*** StateMachineEngineConfig** author wsjz* date 2023/10/23*/
Configuration
public class StateMachineEngineConfig {Autowiredprivate DataSource dataSource;Beanpublic ThreadPoolExecutor threadExecutor() {ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor();//核心线程数executor.setCorePoolSize(1);//最大线程数executor.setMaxPoolSize(20);//线程池中线程的名称前缀executor.setThreadNamePrefix(SAGA_ASYNC_EXE_);//初始化executor.initialize();return executor.getThreadPoolExecutor();}Beanpublic DbStateMachineConfig dbStateMachineConfig() {DbStateMachineConfig stateMachineConfig new DbStateMachineConfig();//设置saga状态机json文件路径stateMachineConfig.setDataSource(dataSource);ClassPathResource resource new ClassPathResource(seata/saga_order.json);stateMachineConfig.setResources(new Resource[]{resource});stateMachineConfig.setEnableAsync(true);stateMachineConfig.setThreadPoolExecutor(threadExecutor());//seata server服务名stateMachineConfig.setApplicationId(seata-server);//事物分组stateMachineConfig.setTxServiceGroup(seata-saga-account-learn-seata-service-group);return stateMachineConfig;}Beanpublic ProcessCtrlStateMachineEngine stateMachineEngine() {ProcessCtrlStateMachineEngine processCtrlStateMachineEngine new ProcessCtrlStateMachineEngine();processCtrlStateMachineEngine.setStateMachineConfig(dbStateMachineConfig());return processCtrlStateMachineEngine;}Beanpublic StateMachineEngineHolder stateMachineEngineHolder() {StateMachineEngineHolder engineHolder new StateMachineEngineHolder();engineHolder.setStateMachineEngine(stateMachineEngine());return engineHolder;}
}OrderController
package com.wsjzzcbq.controller;import io.seata.saga.engine.StateMachineEngine;
import io.seata.saga.statelang.domain.ExecutionStatus;
import io.seata.saga.statelang.domain.StateMachineInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;/*** OrderController** author wsjz* date 2023/10/23*/
RequestMapping(/order)
RestController
public class OrderController {Autowiredprivate StateMachineEngine stateMachineEngine;/*** http://localhost:9002/order/create?userId101money10count1rollbackfalse* param userId* param money* param count* param rollback* return*/RequestMapping(/create)public String create(String userId, int money, int count, boolean rollback) {String orderCode UUID.randomUUID().toString();MapString, Object startParams new HashMap();startParams.put(orderCode, orderCode);startParams.put(userId, userId);startParams.put(money, money);startParams.put(count, count);startParams.put(rollback, rollback);String businessKey String.valueOf(System.currentTimeMillis());StateMachineInstance stateMachineInstance stateMachineEngine.startWithBusinessKey(order, null, businessKey, startParams);if (ExecutionStatus.SU.equals(stateMachineInstance.getStatus())) {return 下单成功;} else {return 下单失败;}}
}application.yml 文件
server:port: 9002
spring:main:allow-circular-references: trueapplication:name: seata-saga-order-learndatasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.3.232:3306/pmc-order?useUnicodetruecharacterEncodingutf8autoReconnecttrueuseSSLfalseserverTimezoneAsia/Shanghaiusername: rootpassword: 123456cloud:nacos:username: nacospassword: nacosserver-addr: 192.168.2.140discovery:namespace: public
# server-addr: 192.168.2.140
# config:
# server-addr:seata:config:type: nacosnacos:server-addr: ${spring.cloud.nacos.server-addr}username: ${spring.cloud.nacos.username}password: ${spring.cloud.nacos.password}group: SEATA_GROUPdata-id: seata-saga.propertiesregistry:type: nacosnacos:application: seata-servercluster: defaultserver-addr: ${spring.cloud.nacos.server-addr}username: ${spring.cloud.nacos.username}password: ${spring.cloud.nacos.password}group: SEATA_GROUP
# 事物分组如果不配置默认是spring.application.name -seata-service-grouptx-service-group: seata-saga-account-learn-seata-service-groupenabled: trueclient:rm:report-success-enable: true
# 是否开启数据源自动代理seata-spring-boot-starter专有配置默认会开启数据源自动代理可通过该配置项关闭enable-auto-data-source-proxy: falselogging:level:com.wsjzzcbq.mapper: debugmybatis-plus:global-config:db-config:id-type: auto
saga 状态机 json 文件
在resources 目录下新建 seata 文件夹在seata 文件夹目录下新建 saga_order.json 文件
saga_order.json 文件内容
{nodes: [{type: node,size: 80*72,shape: flow-rhombus,color: #13C2C2,label: 订单服务结果选择,stateId: OrderService-create-Choice,stateType: Choice,x: 467.875,y: 286.5,id: c11238b3,stateProps: {Type: Choice,Choices: [{Expression: [deductResult] true,Next: AccountService-deductAccount}],Default: Fail},index: 6},{type: node,size: 39*39,shape: flow-circle,color: red,label: 账户服务异常捕获,stateId: AccountService-deductAccount-catch,stateType: Catch,x: 524.875,y: 431.5,id: 053ac3ac,index: 7},{type: node,size: 72*72,shape: flow-circle,color: #FA8C16,label: 开始,stateId: Start,stateType: Start,stateProps: {StateMachine: {Name: order,Comment: saga事物调用,Version: 0.0.1},Next: OrderService-create},x: 467.875,y: 53,id: 973bd79e,index: 11},{type: node,size: 110*48,shape: flow-rect,color: #1890FF,label: 订单服务,stateId: OrderService-create,stateType: ServiceTask,stateProps: {Type: ServiceTask,ServiceName: orderService,Next: AccountService-deduct-Choice,ServiceMethod: create,Input: [$.[orderCode],$.[userId],$.[money],$.[count],$.[rollback]],Output: {deductResult: $.#root},Status: {#root true: SU,#root false: FA,$Exception{java.lang.Throwable}: UN},CompensateState: OrderService-compensateCreate,Retry: []},x: 467.875,y: 172,id: e17372e4,index: 12},{type: node,size: 110*48,shape: flow-rect,color: #1890FF,label: 账户服务,stateId: AccountService-deductAccount,stateType: ServiceTask,stateProps: {Type: ServiceTask,ServiceName: accountService,ServiceMethod: deductAccount,CompensateState: AccountService-compensateDeductAccount,Input: [$.[userId],$.[money],$.[rollback]],Output: {deductResult: $.#root},Status: {#root true: SU,#root false: FA,$Exception{java.lang.Throwable}: UN},Next: Succeed},x: 467.125,y: 411,id: a6c40952,index: 13},{type: node,size: 110*48,shape: flow-capsule,color: #722ED1,label: 订单服务补偿,stateId: OrderService-compensateCreate,stateType: Compensation,stateProps: {Type: Compensation,ServiceName: orderService,ServiceMethod: compensateCreate,Input: [$.[orderCode],$.[userId],$.[money]]},x: 260.625,y: 172.5,id: 3b348652,index: 14},{type: node,size: 110*48,shape: flow-capsule,color: #722ED1,label: 账户服务补偿,stateId: AccountService-compensateDeductAccount,stateType: Compensation,stateProps: {Type: Compensation,ServiceName: accountService,ServiceMethod: compensateDeductAccount,Input: [$.[userId],$.[money],$.[rollback]]},x: 262.125,y: 411,id: 13b600b1,index: 15},{type: node,size: 72*72,shape: flow-circle,color: #05A465,label: 成功,stateId: Succeed,stateType: Succeed,x: 466.625,y: 597.5,id: 690e5c5e,stateProps: {Type: Succeed},index: 16},{type: node,size: 110*48,shape: flow-capsule,color: red,label: 补偿触发器,stateId: CompensationTrigger,stateType: CompensationTrigger,x: 894.125,y: 287,id: 757e057f,stateProps: {Type: CompensationTrigger,Next: Fail},index: 17},{type: node,size: 72*72,shape: flow-circle,color: red,label: 失败,stateId: Fail,stateType: Fail,stateProps: {Type: Fail,ErrorCode: FAILED,Message: buy failed},x: 684.125,y: 287,id: 0131fc0c,index: 18},{type: node,size: 39*39,shape: flow-circle,color: red,label: 订单服务异常捕获,stateId: OrderService-create-catch,stateType: Catch,x: 518.125,y: 183,id: 0955401d}],edges: [{source: 973bd79e,sourceAnchor: 2,target: e17372e4,targetAnchor: 0,id: f0a9008f,index: 0},{source: e17372e4,sourceAnchor: 2,target: c11238b3,targetAnchor: 0,id: cd8c3104,index: 2,label: 执行结果,shape: flow-smooth},{source: c11238b3,sourceAnchor: 2,target: a6c40952,targetAnchor: 0,id: e47e49bc,stateProps: {},label: 执行成功,shape: flow-smooth,index: 3},{source: c11238b3,sourceAnchor: 1,target: 0131fc0c,targetAnchor: 3,id: e3f9e775,stateProps: {},label: 执行失败,shape: flow-smooth,index: 4},{source: 053ac3ac,sourceAnchor: 1,target: 757e057f,targetAnchor: 2,id: 3f7fe6ad,stateProps: {Exceptions: [java.lang.Throwable],Next: CompensationTrigger},label: 账户服务异常触发补偿,shape: flow-polyline-round,index: 5},{source: e17372e4,sourceAnchor: 3,target: 3b348652,targetAnchor: 1,id: 52a2256e,style: {lineDash: 4},index: 8,label: ,shape: flow-smooth},{source: a6c40952,sourceAnchor: 3,target: 13b600b1,targetAnchor: 1,id: 474512d9,style: {lineDash: 4},index: 9},{source: 0955401d,sourceAnchor: 1,target: 757e057f,targetAnchor: 0,id: 654280aa,shape: flow-polyline-round,stateProps: {Exceptions: [java.lang.Throwable],Next: CompensationTrigger},label: 订单服务异常触发补偿},{source: a6c40952,sourceAnchor: 2,target: 690e5c5e,targetAnchor: 0,id: b6bd2f2a,shape: flow-polyline-round},{source: 757e057f,sourceAnchor: 3,target: 0131fc0c,targetAnchor: 1,id: 7ad2f2b9,shape: flow-polyline-round}]
} 2、添加配置
2.1、客户端配置
在nacos上新建 group 是 SEATA_GROUPdata-id 是 seata-saga.properties 的配置内容如下
seata-saga.properties
#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.typeTCP
transport.serverNIO
transport.heartbeattrue
transport.enableTmClientBatchSendRequestfalse
transport.enableRmClientBatchSendRequesttrue
transport.enableTcServerBatchSendResponsefalse
transport.rpcRmRequestTimeout30000
transport.rpcTmRequestTimeout30000
transport.rpcTcRequestTimeout30000
transport.threadFactory.bossThreadPrefixNettyBoss
transport.threadFactory.workerThreadPrefixNettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefixNettyServerBizHandler
transport.threadFactory.shareBossWorkerfalse
transport.threadFactory.clientSelectorThreadPrefixNettyClientSelector
transport.threadFactory.clientSelectorThreadSize1
transport.threadFactory.clientWorkerThreadPrefixNettyClientWorkerThread
transport.threadFactory.bossThreadSize1
transport.threadFactory.workerThreadSizedefault
transport.shutdown.wait3
transport.serializationseata
transport.compressornone#Transaction routing rules configuration, only for the client
service.vgroupMapping.seata-saga-account-learn-seata-service-groupdefault
#If you use a registry, you can ignore it
service.default.grouplist127.0.0.1:8091
service.enableDegradefalse
service.disableGlobalTransactionfalse#Transaction rule configuration, only for the client
client.rm.asyncCommitBufferLimit10000
client.rm.lock.retryInterval10
client.rm.lock.retryTimes30
client.rm.lock.retryPolicyBranchRollbackOnConflicttrue
client.rm.reportRetryCount5
client.rm.tableMetaCheckEnabletrue
client.rm.tableMetaCheckerInterval60000
client.rm.sqlParserTypedruid
client.rm.reportSuccessEnablefalse
client.rm.sagaBranchRegisterEnablefalse
client.rm.sagaJsonParserfastjson
client.rm.tccActionInterceptorOrder-2147482648
client.tm.commitRetryCount5
client.tm.rollbackRetryCount5
client.tm.defaultGlobalTransactionTimeout60000
client.tm.degradeCheckfalse
client.tm.degradeCheckAllowTimes10
client.tm.degradeCheckPeriod2000
client.tm.interceptorOrder-2147482648
client.undo.dataValidationtrue
client.undo.logSerializationjackson
client.undo.onlyCareUpdateColumnstrue
server.undo.logSaveDays7
server.undo.logDeletePeriod86400000
client.undo.logTableundo_log
client.undo.compress.enabletrue
client.undo.compress.typezip
client.undo.compress.threshold64k
#For TCC transaction mode
tcc.fence.logTableNametcc_fence_log
tcc.fence.cleanPeriod1h
# You can choose from the following options: fastjson, jackson, gson
tcc.contextJsonParserTypefastjson#Log rule configuration, for client and server
log.exceptionRate100
seata-saga.properties 在《Spring Cloud Alibaba Seata 实现分布式事物》的 seata.properties 基础上修改事物分组即可 2.2、服务端配置
服务端配置和《Spring Cloud Alibaba Seata 实现分布式事物》保持一致无需修改 3、数据库建表
3.1、seata 服务端建表
看《Spring Cloud Alibaba Seata 实现分布式事物》seata 服务端建表保持一致无需修改 3.2、seata 客户端建表
看《Spring Cloud Alibaba Seata 实现分布式事物》seata 客户端建表
undo_log 表不需要保留 account 和 order_tbl 表即可 3.3、Saga 状态机建表
Saga 状态机表和最外层调用方在同一个库在笔者的项目中和 order服务的库放在一起
建表 sql 在 seata 源码 seata\script\client\saga\db 目录下 建表sql
-- -------------------------------- The script used for sage --------------------------------CREATE TABLE IF NOT EXISTS seata_state_machine_def
(id VARCHAR(32) NOT NULL COMMENT id,name VARCHAR(128) NOT NULL COMMENT name,tenant_id VARCHAR(32) NOT NULL COMMENT tenant id,app_name VARCHAR(32) NOT NULL COMMENT application name,type VARCHAR(20) COMMENT state language type,comment_ VARCHAR(255) COMMENT comment,ver VARCHAR(16) NOT NULL COMMENT version,gmt_create DATETIME(3) NOT NULL COMMENT create time,status VARCHAR(2) NOT NULL COMMENT status(AC:active|IN:inactive),content TEXT COMMENT content,recover_strategy VARCHAR(16) COMMENT transaction recover strategy(compensate|retry),PRIMARY KEY (id)
) ENGINE InnoDBDEFAULT CHARSET utf8mb4;CREATE TABLE IF NOT EXISTS seata_state_machine_inst
(id VARCHAR(128) NOT NULL COMMENT id,machine_id VARCHAR(32) NOT NULL COMMENT state machine definition id,tenant_id VARCHAR(32) NOT NULL COMMENT tenant id,parent_id VARCHAR(128) COMMENT parent id,gmt_started DATETIME(3) NOT NULL COMMENT start time,business_key VARCHAR(48) COMMENT business key,start_params TEXT COMMENT start parameters,gmt_end DATETIME(3) COMMENT end time,excep BLOB COMMENT exception,end_params TEXT COMMENT end parameters,status VARCHAR(2) COMMENT status(SU succeed|FA failed|UN unknown|SK skipped|RU running),compensation_status VARCHAR(2) COMMENT compensation status(SU succeed|FA failed|UN unknown|SK skipped|RU running),is_running TINYINT(1) COMMENT is running(0 no|1 yes),gmt_updated DATETIME(3) NOT NULL,PRIMARY KEY (id),UNIQUE KEY unikey_buz_tenant (business_key, tenant_id)
) ENGINE InnoDBDEFAULT CHARSET utf8mb4;CREATE TABLE IF NOT EXISTS seata_state_inst
(id VARCHAR(48) NOT NULL COMMENT id,machine_inst_id VARCHAR(128) NOT NULL COMMENT state machine instance id,name VARCHAR(128) NOT NULL COMMENT state name,type VARCHAR(20) COMMENT state type,service_name VARCHAR(128) COMMENT service name,service_method VARCHAR(128) COMMENT method name,service_type VARCHAR(16) COMMENT service type,business_key VARCHAR(48) COMMENT business key,state_id_compensated_for VARCHAR(50) COMMENT state compensated for,state_id_retried_for VARCHAR(50) COMMENT state retried for,gmt_started DATETIME(3) NOT NULL COMMENT start time,is_for_update TINYINT(1) COMMENT is service for update,input_params TEXT COMMENT input parameters,output_params TEXT COMMENT output parameters,status VARCHAR(2) NOT NULL COMMENT status(SU succeed|FA failed|UN unknown|SK skipped|RU running),excep BLOB COMMENT exception,gmt_updated DATETIME(3) COMMENT update time,gmt_end DATETIME(3) COMMENT end time,PRIMARY KEY (id, machine_inst_id)
) ENGINE InnoDBDEFAULT CHARSET utf8mb4;
在order服务 pmc-order 库中 4、Saga 状态机 json 文件说明
笔者使用的 seata-server 版本是 1.7.1可以直接打开 http://localhost:7091/ seata 控制台里面有Saga 状态机设计器可以拖拽编辑状态机文件。如果是较低版本的 seata server需要在seata源码中找到 seata-saga-statemachine-designer 项目npm install 安装依赖npm run start 运行项目这个seata-saga-statemachine-designer 是一个单独的Saga 状态机设计器和seata-server -1.7.1 控制台中的一样 将笔者的 saga_order.json 文件复制到状态机设计器中可查看saga事物调用流程 这个流程图定义了saga事物流程 Next指向下一步执行的节点 ServiceName 对应代码中spring的bean名称即 seata-saga-order-learn 中 OrderServiceImplOrderServiceImpl 类上面标记注解 Service(orderService)
ServiceMethod 对应的是 ServiceName下的方法名 Input 是ServiceMethod 方法的参数 Output 是ServiceMethod 方法返回值赋给变量 deductResult
Status 是服务执行状态SU 成功、FA 失败、UN 未知。我们需要把程序执行结果转换成这个3个状态程序返回true对应 SUfalse 对应FA抛出异常是UN
CompensateState 是补偿写补偿节点的 id ServiceName 、 ServiceMethod 和 Input 道理同上发生补偿时触发补偿的方法 捕获异常触发补偿 补偿触发器 程序执行结果判断
Expression 判断程序执行结果
Next 指向下一节点 更多seata内容可以看官网文档https://seata.io/zh-cn/docs/user/saga 5、运行测试
启动 seata-server-1.7.1
进入 bin 目录双击 seata-server.bat 启动 account 和 order 服务 nacos 服务和配置 测试正常情况
浏览器请求http://localhost:9002/order/create?userId101money10rollbackfalse
扣减账户 10 元新增订单 测试回滚情况 6、项目代码
码云地址https://gitee.com/wsjzzcbq/csdn-blog/tree/master/cloud-learn 至此完