先抛出我所遇到的几个问题:
- [imeoutChecker_1] i.s.c.r.netty.NettyClientChannelManager : no available server to connect.
- no available service 'default' found, please make sure registry config corre
- nacos registry, xx register failed...NacosRegistration{nacosDiscoveryProperties=NacosDiscoveryPro...
等等各种小问题,我也是忙活了2天才完全的整合好。亲测有效。一开始我用的是eureka做注册中心的,但是一直报错,配置文件都检查了,报这个错:was unable to refresh its cache! status = There is no known eureka server。最后还是选择用的nacos作为注册中心。
使用Seata分为2部分,首先是服务端的配置和启动。其次是客户端配置和使用
一、seata的安装和配置
1. seata 下载
我们先从官网下载seata-server,这里下载的是seata-server-1.4.0.zip
,下载地址:https://github.com/seata/seata/releases
2、下载nacos
从官网下载nacos,相关配置可自行百度,地址:https://github.com/alibaba/nacos/releases
3、维护seata相关的表
在下载seata的conf目录下面README-zh.md文件有sql下载地址:https://github.com/seata/seata/tree/develop/script/client,https://github.com/seata/seata/tree/develop/script/server
日志表 undo_log 与我们的业务库一起,其他表可以单独放seata库
-- the table to store GlobalSession data
use seata;
drop table if exists `seata.global_table`;
create table `global_table` (
`xid` varchar(128) not null,
`transaction_id` bigint,
`status` tinyint not null,
`application_id` varchar(32),
`transaction_service_group` varchar(32),
`transaction_name` varchar(128),
`timeout` int,
`begin_time` bigint,
`application_data` varchar(2000),
`gmt_create` datetime,
`gmt_modified` datetime,
primary key (`xid`),
key `idx_gmt_modified_status` (`gmt_modified`, `status`),
key `idx_transaction_id` (`transaction_id`)
);
-- the table to store BranchSession data
drop table if exists `branch_table`;
create table `branch_table` (
`branch_id` bigint not null,
`xid` varchar(128) not null,
`transaction_id` bigint ,
`resource_group_id` varchar(32),
`resource_id` varchar(256) ,
`lock_key` varchar(128) ,
`branch_type` varchar(8) ,
`status` tinyint,
`client_id` varchar(64),
`application_data` varchar(2000),
`gmt_create` datetime,
`gmt_modified` datetime,
primary key (`branch_id`),
key `idx_xid` (`xid`)
);
-- the table to store lock data
drop table if exists `lock_table`;
create table `lock_table` (
`row_key` varchar(128) not null,
`xid` varchar(96),
`transaction_id` long ,
`branch_id` long,
`resource_id` varchar(256) ,
`table_name` varchar(32) ,
`pk` varchar(36) ,
`gmt_create` datetime ,
`gmt_modified` datetime,
primary key(`row_key`)
);
我的表结构如下:
4、下载两个文件config.txt和nacos-config.sh
config.txt的下载地址:https://github.com/seata/seata/blob/develop/script/config-center/config.txt
nacos-config.sh 的下载地址:https://github.com/seata/seata/blob/develop/script/config-center/nacos/nacos-config.sh
4.1 nacos-config.sh 这个文件不需要改动,直接拿下载的就可以
4.2 config.txt 部分地方需要修改,如下图所示:
4.3 文件位置比较重要
一定记得把config.txt 放在 seata目录的上级目录,如下所示。
4.4 执行命令
双击:nacos-config.sh这个文件
或者 通过git执行nacos-config.sh脚本
在 nacos-config.sh 右击 git base ,然后执行。注意,namespace默认是public 所以不需要管
sh nacos-config.sh -h 127.0.0.1 -p 9948 -g my_test_tx_group
执行完这个命令,控制台可以看到去初始化一些配置的过程。成功后提示init nacos config finished, please start seata-server
登录nacos查看配置中心,可以看到多了很多配置项:如下:
5 、修改seata服务的配置文件
该步骤是修改seata下面D:\seata\seata\conf 里面的file.conf 和 registry.conf 2个文件。
5.1 修改file.conf
5.2 修改 registry.conf
修改2个地方,一个是注册中心,还有一个是config。如图:
6、启动seata服务
找到路劲:D:\seata\seata\bin,双击seata-server.bat 即可。
二、客户端配置
创建模块seata-order-service模块
1、修改application.yml文件,自定义事务组的名称
2、添加并修改file.conf配置文件,主要是修改自定义事务组名称;
transport {
# tcp udt unix-domain-socket
type = "TCP"
#NIO NATIVE
server = "NIO"
#enable heartbeat
heartbeat = true
#thread factory for netty
thread-factory {
boss-thread-prefix = "NettyBoss"
worker-thread-prefix = "NettyServerNIOWorker"
server-executor-thread-prefix = "NettyServerBizHandler"
share-boss-worker = false
client-selector-thread-prefix = "NettyClientSelector"
client-selector-thread-size = 1
client-worker-thread-prefix = "NettyClientWorkerThread"
# netty boss thread size,will not be used for UDT
boss-thread-size = 1
#auto default pin or 8
worker-thread-size = 8
}
shutdown {
# when destroy server, wait seconds
wait = 3
}
serialization = "seata"
compressor = "none"
}
service {
#vgroup->rgroup
vgroup_mapping.my_test_tx_group = "default"
#only support single node
default.grouplist = "127.0.0.1:8091"
#degrade current not support
enableDegrade = false
#disable
disable = false
#unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
max.commit.retry.timeout = "-1"
max.rollback.retry.timeout = "-1"
disableGlobalTransaction = false
}
client {
async.commit.buffer.limit = 10000
lock {
retry.internal = 10
retry.times = 30
}
report.retry.count = 5
tm.commit.retry.count = 1
tm.rollback.retry.count = 1
}
transaction {
undo.data.validation = true
undo.log.serialization = "jackson"
undo.log.save.days = 7
#schedule delete expired undo_log in milliseconds
undo.log.delete.period = 86400000
undo.log.table = "undo_log"
}
support {
## spring
spring {
# auto proxy the DataSource bean
datasource.autoproxy = false
}
}
3、添加并修改registry.conf配置文件,主要是将注册中心改为nacos;
registry {
# file 、nacos 、eureka、redis、zk
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = ""
cluster = "default"
username = "nacos"
password = "nacos"
}
eureka {
serviceUrl = "http://localhost:1111/eureka"
application = "default"
weight = "1"
}
redis {
serverAddr = "localhost:6381"
db = "0"
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
file {
name = "file.conf"
}
}
config {
# file、nacos 、apollo、zk
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = ""
group = "SEATA_GROUP"
username = "nacos"
password = "nacos"
}
apollo {
app.id = "fescar-server"
apollo.meta = "http://192.168.1.204:8801"
}
zk {
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
file {
name = "file.conf"
}
}
4、pom文件引入相关的jar
<?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 https://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.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.macro.cloud</groupId>
<artifactId>seata-order-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>seata-order-service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR5</spring-cloud.version>
<mysql-connector-java.version>8.0.21</mysql-connector-java.version>
<mybatis-spring-boot-starter.version>2.0.0</mybatis-spring-boot-starter.version>
<druid-spring-boot-starter.version>1.1.22</druid-spring-boot-starter.version>
<lombok.version>1.18.8</lombok.version>
<seata.version>1.4.0</seata.version>
</properties>
<dependencies>
<!--nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>${seata.version}</version>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<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>2.1.0.RELEASE</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>
5、在启动类中取消数据源的自动创建
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@MapperScan(basePackages = {"com.fang.fangseataorder.dao"})
@EnableDiscoveryClient
@EnableFeignClients
public class FangSeataOrderApplication {
public static void main(String[] args) {
SpringApplication.run(FangSeataOrderApplication.class, args);
}
}
6、创建配置使用Seata对数据源进行代理:
@Configuration
public class DataSourceProxyConfig {
@Value("${mybatis.mapperLocations}")
private String mapperLocations;
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource();
}
@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSourceProxy);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(mapperLocations));
sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
return sqlSessionFactoryBean.getObject();
}
}
7、使用@GlobalTransactional注解开启分布式事务
@Service
public class OrderServiceImpl implements OrderService {
private static final Logger LOGGER = LoggerFactory.getLogger(OrderServiceImpl.class);
@Resource
private OrderDao orderDao;
@Autowired
private StorageService storageService;
@Autowired
private AccountService accountService;
/**
* 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
*/
@Override
@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
public void create(Order order) {
LOGGER.info("------->下单开始");
//本应用创建订单
orderDao.create(order);
//远程调用库存服务扣减库存
LOGGER.info("------->order-service中扣减库存开始");
storageService.decrease(order.getProductId(),order.getCount());
LOGGER.info("------->order-service中扣减库存结束");
//远程调用账户服务扣减余额
LOGGER.info("------->order-service中扣减余额开始");
accountService.decrease(order.getUserId(),order.getMoney());
LOGGER.info("------->order-service中扣减余额结束");
//修改订单状态为已完成
LOGGER.info("------->order-service中修改订单状态开始");
orderDao.update(order.getUserId(),0);
LOGGER.info("------->order-service中修改订单状态结束");
LOGGER.info("------->下单结束");
}
}
到此,一个seata服务就搭建完成。启动seata,然后启动order服务。在seata控制台可以看到服务已经注册上了。按照上面的步骤可以创建其他的模块。在seata控制台可以看到服务:
8、调用接口,测试服务
调用接口进行下单操作后查看数据库:http://localhost:7085/order/create?userId=1&productId=1&count=20&money=200
数据库中测试数据 商品1 的数量是100 ,钱1000。访问之后如下图
此时可以查看数据库和控制台信息。发现数据已经发生了变化。控制台也打印了相关的日志。接下来,需要制造异常信息。
我们在fang-seata-account中制造一个超时异常后
/**
* 账户业务实现类
*/
@Service
public class AccountServiceImpl implements AccountService {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class);
@Autowired
private AccountDao accountDao;
/**
* 扣减账户余额
*/
@Override
public void decrease(Long userId, BigDecimal money) {
LOGGER.info("------->account-service中扣减账户余额开始");
//模拟超时异常,全局事务回滚
try {
Thread.sleep(30*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
accountDao.decrease(userId,money);
LOGGER.info("------->account-service中扣减账户余额结束");
}
}
重启服务,再次调用服务。发现数据未发生变化,说明3个服务的事务都已经回滚了。基本上一个完整的seata+nacos的搭建就结束了。
项目地址:https://github.com/fangxiaomin/fang-root 项目是:fang-seata-order、fang-seata-account、fang-seata-storage