1. Seata Server - TC全局事务协调器
分布式事物中:
TM,RM是嵌入在业务应用中,而TC则是一个单独的服务: Seata Server
下载地址: https://github.com/seata/seata/releases
1.1 Seata Server配置文件
1. seata/conf/registry.conf
配置注册中心,那么其他服务就可以通过注册中心去发现 Seata Server,与 Seata Server 进行通信
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
# 这里选择 eureka 注册配置
type = "eureka"
nacos {
......
}
# eureka的注册配置
eureka {
# 注册中心地址
serviceUrl = "http://localhost:8761/eureka"
# 注册的服务ID
application = "seata-server"
weight = "1"
}
redis {
......
}
......
2. seata/conf/file.conf
- Seata 需要存储全局事务信息、分支事务信息、全局锁信息,这些数据进行存储
- 针对存储位置的配置,支持放在配置中心,或者也可以放在本地文件。Seata Server 支持的配置中心服务有:nacos 、apollo、zk、consul、etcd3
- 这里使用本地存储,
先在registry.conf配置储存位置
......
config {
# file、nacos 、apollo、zk、consul、etcd3
# 在这里选择使用本地文件来保存配置
type = "file"
......
file {
# 在这里设置配置文件的文件名
name = "file.conf"
}
}
file.config储存位置可存放在file、db、redis中,这里使用存放在数据库中
store {
## store mode: file、db、redis
# 这里选择数据库存储
mode = "db"
......
# 数据库存储
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
# 数据库连接配置
url = "jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"
user = "root"
password = "root"
minConn = 5
maxConn = 30
# 事务日志表表名设置
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
......
}
1.2 启动Seata Server服务
1. 测试环境修改 /seata/bin/seata-server.bat文件内容(JVM参数)
用文本编辑器打开
%JAVACMD% %JAVA_OPTS% -server -Xmx2048m -Xms2048m -Xmn1024m -Xss512k -XX:Sur......
我们可以把内存调低
%JAVACMD% %JAVA_OPTS% -server -Xmx256m -Xms256m -Xmn128m -Xss512k -XX:Sur......
如果在Linux环境下, 则需要修改 /seata/bin/seata-server.sh文件参数
2. 启动Seata服务
双击seata-server.bat文件启动,检查eureka注册中心Seata的注册信息
如果在linux系统下,需要cd到文件目录下, 通过一下方式启动
sh seata-server.sh
2. 订单模块order
2.1 添加Seata依赖
或者父级项目
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.0.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.3.0</version>
</dependency>
2.2 配置
2.2.1 application.yml
添加向seata注册, 并将当前服务加入一个事物组(要求订单,库存,账户服务所属同一组)
spring:
cloud:
alibaba:
seata:
tx-service-group: order_tx_group
2.2.2 在resource目录下添加registry.conf
需要从注册中心获得 TC 的地址,这里配置注册中心的地址。
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "eureka"
......
eureka {
serviceUrl = "http://localhost:8761/eureka"
# application = "default"
# weight = "1"
}
......
}
2.2.3 在resource目录下添加file.conf
TC 在注册中心注册的服务ID在下面 file.conf 中指定。
在这里我们指定 TC 的服务ID seata-server:vgroupMapping.order_tx_group = “seata-server”
order_tx_group 对应 application.yml 中注册的事务组名。
......
service {
#transaction service group mapping
# order_tx_group 与 yml 中的 “tx-service-group: order_tx_group” 配置一致
# “seata-server” 与 TC 服务器的注册名一致
# 从eureka获取seata-server的地址,再向seata-server注册自己,设置group
vgroupMapping.order_tx_group = "seata-server"
#only support when registry.type=file, please don't set multiple addresses
order_tx_group.grouplist = "127.0.0.1:8091"
#degrade, current not support
enableDegrade = false
#disable seata
disableGlobalTransaction = false
}
......
2.3 创建Seata数据源代理
Seata AT 事务对业务代码无侵入,全自动化处理全局事务,其功能是靠 Seata 的数据源代理工具实现的。
创建 Seata 的数据源代理,并排除 Spring 默认的数据源。
@Configuration
public class DatasourceConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}
@Primary
@Bean("dataSource")
public DataSourceProxy dataSource(DataSource druidDataSource){
return new DataSourceProxy(druidDataSource);
}
}
主程序排除springboot默认数据源
@EnableFeignClients
@MapperScan("cn.tedu.order.mapper")
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
注意: seata数据源代理,数据库连接名为jdbcUrl
需要在application.yml文件中重写配置数据库连接地址
spring:
application:
name: storage
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/seata_storage?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
jdbcUrl: jdbc:mysql://localhost:3306/seata_storage?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
username: root
password: root
2.4 启动全局事物
Seata对象业务无入侵, 所以开启全局事物很简单只需要添加一个@GlobalTransactional 注解即可
注意: 本地分支事物也是在这里开启添加@Transactional
@Service
public class OrderServiceImpl implements OrderService{
@Transactional //控制本地事物
@GlobalTransactional //开启全局事物,只在第一个模块添加
@Override
//创建订单方法
public void create(Order order) {
......
创建订单方法: 调用orderMapper实现订单数据存储,远程调用库存,账户,实现入库操作
3. 库存模块storage
3.1 添加依赖
如果父级项目添加这里不用添加
3.2 配置
3.2.1 application.yml
与订单order业务一致
3.2.2 registry.conf , file.conf
与订单order业务一致
3.3 创建Seata数据源对象
与订单order业务一致
3.4 启动分支事物
在业务方法上添加@Transactional 注解启动本地分支事务
@Service
public class StorageServiceImpl implements StorageService {
@Autowired
private StorageMapper storageMapper;
@Transactional
@Override
public void decrease(Long productId, Integer count) throws Exception {
storageMapper.decrease(productId,count);
}
}
4. 账户模块
与库存模块一致
5. 启动三个服务测试
按顺序启动项目:
Eureka
Seata Server
Easy Id Generator
Storage
Account
Order
调用保存订单,地址:
http://localhost:8083/create?userId=1&productId=1&count=10&money=100
观察三个服务,查看控制台
可以观察到全局事物id(XID),和本地事物id(Branch ID)