示例代码地址:https://gitee.com/gtnotgod/Springcloud-alibaba.git
更详细参考Gitee完整的项目:https://gitee.com/gtnotgod/Springcloud-alibaba.git
官网下载Nacos
压缩包解压:

配置Nacos:**/nacos/conf/application.properties
#*************** Spring Boot Related Configurations ***************# ### Default web context path: server.servlet.contextPath=/nacos ### Include message field server.error.include-message=ALWAYS ### Default web server port: server.port=8848 ### Metrics for elastic search management.metrics.export.elastic.enabled=false #management.metrics.export.elastic.host=http://localhost:9200 #*************** Access Log Related Configurations ***************# ### If turn on the access log: server.tomcat.accesslog.enabled=true ### The access log pattern: server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i ### The directory of access log: server.tomcat.basedir=file:. #*************** Access Control Related Configurations ***************# ### If enable spring security, this option is deprecated in 1.2.0: #spring.security.enabled=false ### The ignore urls of auth nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/** ### The auth system to use, currently only 'nacos' and 'ldap' is supported: nacos.core.auth.system.type=nacos ### If turn on auth system: ### 开启鉴权 nacos.core.auth.enabled=true ### Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay. nacos.core.auth.caching.enabled=true ### 关闭使用user-agent判断服务端请求并放行鉴权的功能 nacos.core.auth.enable.userAgentAuthWhite=false ### Since 1.4.1, worked when nacos.core.auth.enabled=true and nacos.core.auth.enable.userAgentAuthWhite=false. ### The two properties is the white list for auth and used by identity the request from other server. nacos.core.auth.server.identity.key=nacos nacos.core.auth.server.identity.value=nacos ### The token expiration in seconds: nacos.core.auth.plugin.nacos.token.cache.enable=false nacos.core.auth.plugin.nacos.token.expire.seconds=18000 ### The default token (Base64 String): nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789 ### 2.1.0 版本后 nacos.core.auth.plugin.nacos.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789 nacos.istio.mcp.server.enabled=false
启动Nacos:进入目录/nacos/bin
startup.cmd -m standalone
启动完成,

访问: http://localhost:8848/nacos

访问:账号密码 nacos nacos

官网下载Seata

本地解压

初始化数据库:进入seatascriptserverdb

配置Naco配置中心和Nacos注册中心,Mysql数据库
seataconfapplication.yml
server: port: 7091 spring: application: name: seata-server logging: config: classpath:logback-spring.xml file: path: ${user.home}/logs/seata extend: logstash-appender: destination: 127.0.0.1:4560 kafka-appender: bootstrap-servers: 127.0.0.1:9092 topic: logback_to_logstash console: user: username: seata password: seata seata: service: vgroup-mapping: my-seata-group: default config: type: nacos nacos: server-addr: 127.0.0.1:8848 group : "SEATA_GROUP" namespace: "70180ace-e644-4a10-b590-e6a6003b1bbe" username: "nacos" password: "nacos" data-id: seataServer.properties registry: type: nacos nacos: application: seata-server server-addr: 127.0.0.1:8848 group : "SEATA_GROUP" namespace: "70180ace-e644-4a10-b590-e6a6003b1bbe" username: "nacos" password: "nacos" store: # support: file 、 db 、 redis # 注意数据库版本为5.7.26 , 使用8.0.12时报错Could not retrieve transation read-only status server mode: db db: datasource: druid db-type: mysql driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata1.6.1?rewriteBatchedStatements=true&useUnicode=true user: root password: root min-conn: 5 max-conn: 100 global-table: global_table branch-table: branch_table lock-table: lock_table distributed-lock-table: distributed_lock query-limit: 100 max-wait: 5000 # server: # service-port: 8091 #If not configured, the default is '${server.port} + 1000' security: secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 tokenValidityInMilliseconds: 1800000 ignore: urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
启动Seata ,双击

出现这个

Nacos服务列表

出现这个

Nacos控制台配置 tx-service-group

出现这个

springcloud项目集成Nacos和Seata
项目公共依赖:
公共依赖lombok、web、MySQL、mybatisplus、dynamicDataSource、knife4j、bootstrap、loadbalancer
<properties> <mybatis-plus.version>3.5.1</mybatis-plus.version> <com.alibaba.druid.version>1.2.11</com.alibaba.druid.version> <nacos-client.version>2.0.4</nacos-client.version> <fastJson-version>2.0.18</fastJson-version> </properties> <dependencies> <!--lombok-实体类简化依赖--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--web项目驱动--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--Mysql数据库--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${com.alibaba.druid.version}</version> </dependency> <!--Mybatis ORM相关依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>${mybatis-plus.version}</version> </dependency> <!--Mybatis-plus 代码生成器--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatisplus.verison}</version> </dependency> <!-- mybatis-plus默认模板引擎--> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>${velocity.engine.version}</version> </dependency> <!--Mybatis-plus 多数据源--> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>${mybatisplus.verison}</version> </dependency> <!--Swagger2--> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>${knife4j.version}</version> </dependency> <!-- bootstrap最高级启动配置读取 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency> <!--新版的移除了Ribbon的负载策略,所需改用新版的loadbalancer--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> <dependency> <groupId>com.alibaba.fastjson2</groupId> <artifactId>fastjson2</artifactId> <version>${fastJson-version}</version> </dependency> </dependencies>
项目一 AppUserManage :10085
依赖
<dependencies> <!--分布式事务解决方案(阿里巴巴seata)--> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>${seata.version}</version> </dependency> <!--If your project base on `Spring Boot`, you can directly use the following dependencies--> <!--Notice: `seata-spring-boot-starter` has included `seata-all` dependency--> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>${seata.version}</version> </dependency> <!--分布式事务解决方案(阿里巴巴seata)--> <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> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> </exclusion> </exclusions> </dependency> <!--微服务项目公共依赖lombok、web、MySQL、mybatisplus、dynamicDataSource、knife4j、bootstrap、loadbalancer--> <dependency> <groupId>com.gton</groupId> <artifactId>common-dependce</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!--nacos服务发现--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--Nacos配置中心--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!--Springcloud微服务启动--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter</artifactId> </dependency> <!--全局最高配置加载--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> <version>3.1.1</version> </dependency> <!--Dubbo的RPC框架--> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> </dependency> <!--Springboot的dubbo适配--> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> </dependency> <!--Dubbo适配naocs--> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-registry-nacos</artifactId> </dependency> <!--nacos2的客户端--> <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!-- redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </exclusion> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <!--Redis使用公共依赖--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> </dependencies>
核心配置 bootstrap.yml
# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html # Nacos 配置中心的namespace。需要注意,如果使用 public 的 namcespace ,请不要填写这个值,直接留空即可 # spring.cloud.nacos.config.namespace= spring: application: #服务自动发现并注册,不需要name name: user-manager cloud: alibaba: seata: tx-service-group: my-seata-group nacos: discovery: server-addr: ${spring.cloud.nacos.server-addr} # 设置配置中心服务端地址 group: AlibabaCloud username: nacos password: nacos namespace: 70180ace-e644-4a10-b590-e6a6003b1bbe config: username: ${spring.cloud.nacos.discovery.username} # Nacos认证信息用户名 password: ${spring.cloud.nacos.discovery.password} # Nacos认证信息密码 context-path: /nacos # Nacos根路径 enabled: true server-addr: ${spring.cloud.nacos.server-addr} group: ${spring.cloud.nacos.discovery.group} namespace: 70180ace-e644-4a10-b590-e6a6003b1bbe file-extension: properties server-addr: 127.0.0.1:8848 # 设置配置中心服务端地址 seata: enabled: true application-id: ${spring.application.name} # 客户端和服务端在同一个事务组; Seata 事务组编号,用于 TC 集群名, 一定要和 config.tx(nacos) 中配置的相同 tx-service-group: my-seata-group # 自动数据源代理 enable-auto-data-source-proxy: true # 数据源代理模式(分布式事务方案) data-source-proxy-mode: AT # 事务群组,配置项值为TC集群名,需要与服务端保持一致 service: vgroup-mapping: my-seata-group: default #整合nacos配置中心 config: type: nacos nacos: server-addr: ${spring.cloud.nacos.server-addr} group: SEATA_GROUP namespace: 70180ace-e644-4a10-b590-e6a6003b1bbe data-id: seataServer.properties username: nacos password: nacos #整合nacos注册中心 registry: type: nacos nacos: server-addr: ${spring.cloud.nacos.server-addr} group: SEATA_GROUP namespace: 70180ace-e644-4a10-b590-e6a6003b1bbe # 默认TC集群名 cluster: default # 服务名,与服务端中registry.conf配置要一致 application: seata-server username: nacos password: nacos dubbo: # 配置元数据中心 metadata-report: address: nacos://127.0.0.1:8848?username=${dubbo.metadata-report.username}&password=${dubbo.metadata-report.password} username: nacos password: nacos parameters: namespace: 70180ace-e644-4a10-b590-e6a6003b1bbe retry-times: 30 #重试次数,默认100 cycle-report: false #关闭定时刷新 application: name: dubbo-consumer # 禁用QOS同一台机器可能会有端口冲突现象 qos-enable: false qos-accept-foreign-ip: false service-discovery: migration: FORCE_APPLICATION # FORCE_APPLICATION,只消费应用级地址,如无地址则报错,单订阅 3.x 地址 protocol: name: dubbo port: -1 scan: base-packages: com.gton.router.impl cloud: subscribed-services: consumer registry: address: nacos://127.0.0.1:8848?username=${dubbo.metadata-report.username}&password=${dubbo.metadata-report.password} parameters: namespace: 70180ace-e644-4a10-b590-e6a6003b1bbe consumer: check: false
启动类
package com.gton; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * @description: APP-Usermanage服务 * @author: GuoTong * @createTime: 2022-09-24 13:48 * @since JDK 1.8 OR 11 **/ @SpringBootApplication @EnableDiscoveryClient //服务发现客户端 public class AppUserManage { public static void main(String[] args) { /** * Description: * 数据库连接全部使用mybatis-plus安全加密,加密密匙: * 在启动参数 (Program arguments) --mpw.key=4b57e89bac82a797 */ try { SpringApplication.run(AppUserManage.class, args); } catch (Exception e) { e.printStackTrace(); } } }
测试接口 包含:Dubbo的RPC调用,Seata分布式事务的测试
/** * @description: * @author: GuoTong * @createTime: 2022-09-24 14:37 * @since JDK 1.8 OR 11 **/ @RestController @Api(tags = "测试接口") @SwaggerScanClass @Slf4j public class HelloController { @Value("${user.name:zhangsan}") private String name; @Value("${user.age:100}") private int age; @Autowired private GpLoginService gpLoginService; /** * Description: Dubbo RPC调用服务方 * * @author: GuoTong * @date: 2022-09-24 17:50:08 * @param null * @return: */ @Autowired private DubboRPCThirdSysRouterService dubboRPCThirdSysRouterService; @RequestMapping(value = "/hello", method = RequestMethod.GET) @ApiOperation(value = "hello", notes = "json格式") @ApiResponse(code = 200, message = "返回的是json格式", response = Resp.class) public Resp hello() { Map<String, Object> data = new HashMap<>(); data.put("call-UrI", "hello"); data.put("port", "8089"); data.put("service-Name", "cloud-nacos"); data.put("server-type", "nacos"); data.put("server-addr", "127.0.0.1:8848"); data.put("swagger-Uri", "doc.html"); data.put("nacos-properties-name", name); data.put("nacos-properties-age", age); return Resp.Ok(data); } @RequestMapping(value = "/dubbo_get_thirdsysrouter", method = RequestMethod.GET) @ApiOperation(value = "usermanager服务调用thirdsysrouter", notes = "json格式") @ApiResponse(code = 200, message = "返回的是json格式", response = Resp.class) public Resp getThirdSysRouterInDubbo(HttpServletRequest request) { String traceId = MDC.get(ContextCommonMsg.TRACE_ID); String username = request.getParameter("username"); if (StringUtils.isEmpty(username)) { username = "郭童"; } return dubboRPCThirdSysRouterService.getUserManagerService_Hello(traceId, username); } @GlobalTransactional @RequestMapping(value = "/dubbo_seata_thirdsysrouter", method = RequestMethod.GET) @ApiOperation(value = "usermanager服务调用thirdsysrouter分布式事务", notes = "json格式") @ApiResponse(code = 200, message = "返回的是json格式", response = Resp.class) public Resp getThirdSysRouterInDubboBySeata(HttpServletRequest request) { String traceId = MDC.get(ContextCommonMsg.TRACE_ID); String username = request.getParameter("username"); if (StringUtils.isEmpty(username)) { username = "郭童"; } // 插入本地数据库 GpLogin gpLogin = new GpLogin(). setUsername("GlobalTransactional" + username) .setPassword("dubbo_seata_thirdsysrouter") .setWelcomeName("分布式事务" + traceId); gpLoginService.save(gpLogin); log.info("第一段插入数据库study库成功" + gpLogin.getId()); Resp resp = dubboRPCThirdSysRouterService.databaseUpdate(gpLogin); log.info("分布式事务----结束,第二段插入数据库study02-id={},响应码=" + resp.getCode(), resp.getData()); int j = 100 / 0; return resp; } }
Seata的核心注解: 全局AT的undolog依赖关系型数据库的事务 开启@GlobalTransactional
Dubbo的核心注解: 调用方:@DubboReference 被调方: @DubboService
省略。。。。。。。。
项目二 ThirdSysRouterApp :8099
配合+依赖和项目一类似,使用不同的数据库和一张表。。。。。。。。。
项目一的Dubbo调用项目二的方法
/** * @description: 基于dubbo实现远程过程调用 * @author: GuoTong * @createTime: 2022-09-24 17:40 * @since JDK 1.8 OR 11 **/ @Service @DubboService @Slf4j public class DubboRouterService implements RemoteUserManageService { @Autowired private GotoUserManagerService gotoUserManagerService; @Autowired private StudentService studentService; /** * Description: Dubbo的消息提供方 @DubboService * * @author: GuoTong * @date: 2022-09-24 17:42:36 */ @Override public Resp<Map<String, Object>> getUserManagerService_Hello(String TraceId, String userName) { log.info("[{}]dubbo 方式请求进入了!!!!", TraceId); Resp<Map<String, Object>> mapData = gotoUserManagerService.createMapData(); Map<String, Object> data = mapData.getData(); data.put("TraceId", TraceId); data.put("userName", userName); return mapData; } @Override public Resp databaseUpdate(Object gpLogin) { Student gpLogin2 = null; if (gpLogin instanceof GpLogin) { Long id = ((GpLogin) gpLogin).getId(); if (id != null && id > 0) { // 插入本地数据库 gpLogin2 = new Student(). setUsername("thirdsysrouter") .setPassword("thirdsysrouter") .setPwdShow("分布式事务thirdsysrouter"); studentService.save(gpLogin2); // 执照异常,利用分布式事务回滚 int i = 100 / 0; } } return gpLogin2 != null ? Resp.Ok(gpLogin2.getId()) : Resp.error("利用分布式事务"); } }
启动两个项目演示

输入项目一调用项目二的SwaggerUI接口测试地址
http://localhost:10085/doc.html#/default/测试接口/getThirdSysRouterInDubboBySeataUsingGET

执行调用
分析响应结果,由于手动制造了异常,被全局异常捕获,响应结果成功

查看A库A表和B库B表是否插入成功
A库A表没有插入成功:分布式事务关键字的一条记录

B库B表没有插入成功:分布式事务关键字的一条记录

数据库结果是成功的,同时回滚了。。。
查看日志
A服务请求进入。执行了A库A表插入成功

继续日志Dubbo 去RPC调用B服务了
2023-07-07 18:23:43.322 INFO 14568 --- [io-10085-exec-9] c.g.s.DubboRPCThirdSysRouterService : [7db0c75b-8a65-4d31-84cb-6cdd14d580a4] [Seata]:dubbo RPC方式请求发送出去!!!!
B服务执行方法;B库B表插入成功响应-->A系统

A系统收到B'系统的响应:

A系统收到B'系统的响应继续走:触发除零异常

A系统:触发全局异常处理


中间触发了这几个Seata二阶段提交的关键字日志

这时候意思很明显 失败回滚;去掉除零异常再测试
代码优化一下:由前端传入参数控制是否触发异常区回滚

测试:界面返回操作成功

测试:数据库是否操作成功
B系统:

A系统

验证通过。。。
再测试失败全局事物回滚::

结束。。。。。。。。。。。。
更详细参考Gitee完整的项目:https://gitee.com/gtnotgod/Springcloud-alibaba.git