目录
1.seata原理
2.关键组件
3.seate服务端参数
4.微服务配置
5.业务流程
6.依次启动eureka、seate服务器、微服务
1.seata原理
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC(类似2PC)、SAGA(长事务解决方案、基于状态机),为用户打造一站式的分布式解决方案。
AT模式同tx-lcn类似,通过代理底层数据库连接,控制数据库事务的提交与回滚(必须依赖数据库);当然seate由于是由阿里维护的,同时支持多种注册中心,zookeeper、eureka等。
![](https://img-blog.csdnimg.cn/20200703145455506.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xlYWRzZWN6Z3cwMQ==,size_16,color_FFFFFF,t_70)
名词解释: TC 事务协调者、TM 事务管理器、RM 资源管理器、Microservices 微服务、DB 数据库
参考链接:
seate主项目:GitHub - seata/seata: Seata is an easy-to-use, high-performance, open source distributed transaction solution.
seate模板:seata-samples/springcloud-eureka-feign-mybatis-seata at master · seata/seata-samples · GitHub;
seate快速使用:Quick Start
2.关键组件
eureka、seate-0.8.0服务端、微服务1(hello)、微服务2(feign-consumer)
spring boot:2.1.8.RELEASE
spring cloud:Greenwich.SR2
代码链接:GitHub - kickTec/springCloudDemo at transaction-seate
csdn链接:transaction-seate.rar-Java文档类资源-CSDN下载
3.seate服务端参数
主要指定host,多个网卡自动绑定IP有时不对
@Parameter(names = "--help", help = true)
private boolean help;
@Parameter(names = {"--host", "-h"}, description = "The ip to register to registry center.", order = 1)
private String host;
@Parameter(names = {"--port", "-p"}, description = "The port to listen.", order = 2)
private int port = SERVER_DEFAULT_PORT;
@Parameter(names = {"--storeMode", "-m"}, description = "log store mode : file, db", order = 3)
private String storeMode;
@Parameter(names = {"--serverNode", "-n"}, description = "server node id, such as 1, 2, 3.it will be generated according to the snowflake by default", order = 4)
private Long serverNode;
@Parameter(names = {"--seataEnv", "-e"}, description = "The name used for multi-configuration isolation.",
order = 5)
windows启动seate服务器
seata-server.bat -h 192.168.0.18
![](https://img-blog.csdnimg.cn/20200703134822127.png)
4.微服务配置
maven配置
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.kenick</groupId>
<artifactId>feign-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>feign-consumer</name>
<description>feign-consumer</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- pageHelper分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.0.0</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.1.0.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.2.0</version>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
seate特殊配置(seate服务器和微服务指定相同的tx-service-group):
spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group
file.conf文件中设置:
#transaction service group mapping
vgroupMapping.my_test_tx_group = "default"
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "192.168.0.18:8091"
register.conf中指定使用eureka:
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "eureka"
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
}
eureka {
serviceUrl = "http://192.168.0.105:8888/eureka"
application = "default"
weight = "1"
}
由于seate AT模式是通过代理数据库连接方式实现的,在每个service服务中都需要进行如下配置:
/**
* 数据源代理
*/
@Configuration
public class DataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource();
}
@Primary
@Bean("dataSource")
public DataSourceProxy dataSource(DataSource druidDataSource){
return new DataSourceProxy(druidDataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy)throws Exception{
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSourceProxy);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mybatis/mapping/*Mapper.xml"));
sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
return sqlSessionFactoryBean.getObject();
}
}
全局事务使用,在service上添加注释
@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
@Override
public int saveUser(String userId, String name, int age) {
logger.debug("本地开始保存用户!");
User user = new User();
user.setUserId(userId);
user.setName(name);
user.setAge(age);
int ret = userMapper.insert(user);
logger.debug("本地开始保存用户结果:" + ret);
logger.debug("开始远程调用helloservice!");
String remoteRet = helloService.addUser("remote_hello" + userId, name, age);
logger.debug("远程调用helloservice结果:" + remoteRet);
// 产生异常
// int num = 1/0;
return ret;
}
其它的请参考项目代码配置。
5.业务流程
调用feign-consumer工程的addUser接口,接口逻辑:先在本地保存用户,再调用hello服务的保存用户接口,相当于一次调用保存2了个用户信息。
6.依次启动eureka、seate服务器、微服务
正常流程
调用feign-consumer接口(192.168.0.18:4001/addUser),先在当前服务添加用户,再调用另一个微服务hello添加用户;2个用户信息添加成功;全局事务执行情况见下图。
feign-consumer日志
![](https://img-blog.csdnimg.cn/20200703143020942.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xlYWRzZWN6Z3cwMQ==,size_16,color_FFFFFF,t_70)
hello日志
![](https://img-blog.csdnimg.cn/20200703143142694.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xlYWRzZWN6Z3cwMQ==,size_16,color_FFFFFF,t_70)
异常流程:两次用户都保存成功后,后续feign-consumer 服务异常,最终两个工程都进行了回退。
feign-consumer日志
![](https://img-blog.csdnimg.cn/20200703144131371.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xlYWRzZWN6Z3cwMQ==,size_16,color_FFFFFF,t_70)
hello日志
![](https://img-blog.csdnimg.cn/20200703144317106.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xlYWRzZWN6Z3cwMQ==,size_16,color_FFFFFF,t_70)