SpringCloudAlibaba整合分布式事务Seata
1 整合分布式事务Seata
1.1 环境搭建
1.1.1 Nacos搭建
1.1.2 Seata搭建
1.2 项目搭建
1.2.1 项目示意
如下图中,alibaba-demo为外层父目录模块,call为调用模块,common为存放共同依赖实体模块,order为支持crud模块
1.2.2 pom.xml
1.2.2.1 alibaba-demo模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>alibaba-demo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>common</module>
<module>order</module>
<module>call</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.boot.version>2.6.11</spring.boot.version>
<spring.cloud.version>2021.0.4</spring.cloud.version>
<spring.cloud.alibaba>2021.0.4.0</spring.cloud.alibaba>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
1.2.2.2 call模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>alibaba-demo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>call</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
1.2.2.3 order模块
该模块中引入 spring-cloud-starter-alibaba-seata
模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>alibaba-demo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
</dependencies>
</project>
1.2.2.4 common模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>alibaba-demo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
</dependencies>
</project>
1.2.3 配置文件
1.2.3.1 order模块
server:
port: 8000
spring:
application:
name: cloud-order-demo
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
username: xxxx
password: xxxx
type: com.alibaba.druid.pool.DruidDataSource
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: xxxx
password: xxxx
mybatis-plus:
mapper-locations: 'classpath:mapper/*Mapper.xml'
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
seata:
enabled: true
tx-service-group: mygroup
data-source-proxy-mode: AT
service:
vgroup-mapping:
mygroup: default
grouplist:
default: 127.0.0.1:8091
registry:
nacos:
application: my-stata-server
group: SEATA_GROUP
namespace: 5658186c-c382-453a-843e-6a490cc80480
username: nacos
password: nacos
server-addr: 127.0.0.1:8848
config:
nacos:
group: SEATA_GROUP
namespace: 5658186c-c382-453a-843e-6a490cc80480
username: nacos
password: nacos
server-addr: 127.0.0.1:8848
# 此处的data-id 和服务端上的一致,服务端怎么配这里也怎么配
data-id: seataServer.properties
注意:
- 上面的
seata.tx-service-group: 自定义值
要和它自己seata.service.vgroup-mapping.自定义值:default
中自定义值
相对应
还要和 服务端的seata.properties
中的service.vgroupMapping.自定义值=default
中自定义值
相对应 - 上面的
seata.service.vgroup-mapping.自定义值:default
要和服务端的seata.properties
的service.default.grouplist=127.0.0.1:8091
中的default
相对应 - 如果启动有error报错:不能找到service.vgroupMapping.自定义值 的服务,可以把
service.vgroupMapping.
单独配置指定
1.2.3.2 call模块
server:
port: 7000
spring:
application:
name: cloud-call
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
logging:
level:
# feign日志以什么级别监控哪个接口
cn.feign: debug
1.2.4 OpenFeign调用
在call模块中
调整feign日志级别
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
feign调用接口
import cn.entity.User;
import cn.feign.fallback.UserFeignFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
@FeignClient(value = "cloud-order-demo",fallbackFactory = UserFeignFallbackFactory.class,path = "/user")
public interface UserFeignService {
@PostMapping("/query")
public List<User> queryAll();
@PostMapping("/save")
public boolean save(User user);
}
feign回调工厂接口,可以打印详细堆栈信息
import cn.entity.User;
import cn.feign.UserFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
@Slf4j
@Component
public class UserFeignFallbackFactory implements FallbackFactory<UserFeignService> {
@Override
public UserFeignService create(Throwable cause) {
log.error("异常原因:{}", cause.getMessage(), cause);
return new UserFeignService(){
@Override
public List<User> queryAll() {
return null;
}
@Override
public boolean save(User user) {
return false;
}
};
}
}
1.2.5 order模块
controller
@RequestMapping("/user")
@RestController
public class UserController {
@Autowired
private IUserService userService;
@PostMapping("/query")
public List<User> query (){
List<User> list = userService.list();
return list;
}
@PostMapping("/save")
@GlobalTransactional
@GlobalLock
public boolean save (@RequestBody User user){
boolean save = userService.save(user);
if(save){
long count = userService.count();
long i = count / 0;
}
return save;
}
@PostMapping("/add")
public int add (@RequestBody User user){
int i = userService.insertUser(user);
return i;
}
}
后续的server以及mapper模块都是集成mybatisplus,没有任何代码,因此不在赘述
1.3 jdk启动问题
1.3.1 问题现象
如果是用的 jdk
是在 9
以上,启动时可能报错,如下:
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @45752059
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) ~[na:na]
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) ~[na:na]
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199) ~[na:na]
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193) ~[na:na]
at net.sf.cglib.core.ReflectUtils$2.run(ReflectUtils.java:56) ~[cglib-3.1.jar:na]
at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) ~[na:na]
at net.sf.cglib.core.ReflectUtils.(ReflectUtils.java:46) ~[cglib-3.1.jar:na]
... 39 common frames omitted
1.3.2 解决问题
报这个错误是由于:这是由于 JDK 8
中有关反射相关的功能自从 JDK 9
开始就已经被限制了,为了兼容原先的版本,需要在运行项目时添加 --add-opens java.base/java.lang=ALL-UNNAMED
选项来开启这种默认不被允许的行为。
这个参数是 Java 9
及更高版本中引入的一个模块化系统(Jigsaw)的特性。它允许你在启动 Java
应用程序时,打开某个模块的某个包,使得其他模块可以访问这个包中的类和成员,这个参数通常用于解决Java 9
及更高版本中,由于模块化系统导致的访问限制问题。例如,一些库可能需要访问 Java
基础模块中的某些类和成员,但在默认情况下,这些访问可能会受到限制。通过添加这个参数,可以确保这些库能够正常工作。
具体来说,--add-opens
参数的作用是:
java.base/java.lang
:表示要打开的模块和包。这里是Java
基础模块(java.base)中的java.lang
包。ALL-UNNAMED
:表示允许哪些模块访问这个包。这里是所有未命名的模块,即没有模块化的代码。