分布式事务的由来
单体应用,一台服务器上的后台系统,对应一个数据库;
分布式应用,每个单独的应用对应一个独立的数据源,每个服务内部
的数据一致性由本地事务保证,但是全局
数据一致性无法得到保障!
例如:
1、仓储服务:对给定的商品扣除仓储数量。
2、订单服务:根据采购需求创建订单。
3、账户服务:从用户账户中扣除余额。
单个的事务,都能保证;但聚合微服务操作后,不同数据库、不同机器等原因,无法统一的保证事务要求。
一次业务操作需要跨多个数据源或者需要跨多个系统进行rpc远程调用,就会产生
分布式事务
问题!
Seata简介
Seata是alibaba开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
Seata 中文官网
典型的分布式事务过程
一个典型的分布式事务过程,一般分为一个ID
和三个组件模型
。
- 一ID:
Transaction ID XID
全局唯一事务ID。 - 三个组件模型:
Transaction Coordinator (TC) 事务协调者
维护全局和分支事务的状态,负责协调并驱动全局事务提交或者回滚。Transaction Manager (TM) 事务管理器
控制全局事务的边界,负责开启一个全局事务,并最终发起全局事务的提交或者全局回滚的决议。Resource Manager (RM) 资源管理器
控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交或回滚。
seata全局事务的处理过程:
1、TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的ID。
2、XID 在微服务调用链路的上下文中传播。
3、RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖。
4、TM 向 TC 发起针对XID的全局提交或回滚决议。
5、TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。
seata 下载和安装
- 1、seata的下载
本次学习使用:seata-server-0.9.0.zip
-
2、seata的安装
解压下载好的seata-server-0.9.0.zip
即可。
-
3、seata的配置
解压完成后,需要修改\seata\conf
中的file.conf
。
主要修改内容:
1、自定义事务组名称。server 中 vgroup_mapping.my_test_tx_group 的名称,随意定义
2、事务日志存储模式为db。(默认为文件)store中的mode
3、数据库连接信息。store中db的连接信息
修改部分代码:
service {
#vgroup->rgroup
# 自定义事务组名称
vgroup_mapping.my_test_tx_group = "xiangjiao_tx_group"
# vgroup_mapping.my_test_tx_group = "default"
}
// --------- 省略其他配置项 ------
## transaction log store
store {
## store mode: file、db
# mode = "file"
# 修改存储方式为db,默认file
mode = "db"
## database store
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = "dbcp"
## mysql/oracle/h2/oceanbase etc.
db-type = "mysql"
driver-class-name = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://数据库所在服务器ip地址:3306/seata"
user = "root"
password = "你的mysql密码"
min-conn = 1
max-conn = 3
global.table = "global_table"
branch.table = "branch_table"
lock-table = "lock_table"
query-limit = 100
}
}
-
4、seata数据库的导入
导入指定的mysql中后,如下所示:
这三张表有各自的用途:
global_table
:全局事务表,每当有一个全局事务发起后,就会在该表中记录全局事务的ID
branch_table
:分支事务表,记录每一个分支事务的 ID,分支事务操作的哪个数据库等信息
lock_table
:全局锁 -
5、修改
conf/registry.conf
配置文件
## 修改注册地址至nacos
#type = "file"
type = "nacos"
nacos {
serverAddr = "localhost:8848"
namespace = ""
cluster = "default"
}
seata的启动
由于配置了注册至nacos
,所以必须先启动nocos
,再启动seata-server
。
关于事务
- 本地事务
@Transactional
- 全局事务
@GlobalTransactional
seata测试demo
1、数据库和表的建立
为了简单验证是否具备了分布式事务
,只需要建立两个(及以上)不同的数据库实现事务操作即可。
创建三个不同的数据库,分别存储订单
、用户余额信息
、库存信息
CREATE DATABASE seata_account;
CREATE DATABASE seata_order;
CREATE DATABASE seata_storage;
seata_order
中创建t_order
订单数据库
CREATE TABLE t_order(
id BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
user_id BIGINT(11) DEFAULT NULL COMMENT '用户id',
product_id BIGINT(11) DEFAULT NULL COMMENT '产品id',
count INT(11) DEFAULT NULL COMMENT '数量',
money DECIMAL(11,0) DEFAULT NULL COMMENT '金额',
status INT(1) DEFAULT NULL COMMENT '订单状态:0创建中,1已完结'
)ENGINE=InnoDB AUTO_INCREMENT=7 CHARSET=utf8;
seata_storage
中创建t_storage
库存数据库
CREATE TABLE t_storage(
id BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
product_id BIGINT(11) DEFAULT NULL COMMENT '产品id',
total INT(11) DEFAULT NULL COMMENT '总库存',
used INT(11) DEFAULT NULL COMMENT '已用库存',
residue INT(11) DEFAULT NULL COMMENT '剩余库存'
)ENGINE=InnoDB AUTO_INCREMENT=7 CHARSET=utf8;
INSERT INTO t_storage(id, product_id, total, used, residue) VALUES(1,1,100,0,100);
seata_account
中创建t_account
账户信息数据库
CREATE TABLE t_account(
id BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
user_id BIGINT(11) DEFAULT NULL COMMENT '用户id',
total DECIMAL(10,0) DEFAULT NULL COMMENT '总额度',
used DECIMAL(10,0) DEFAULT NULL COMMENT '已用额度',
residue DECIMAL(10,0) DEFAULT 0 COMMENT '剩余可用额度'
)ENGINE=InnoDB AUTO_INCREMENT=7 CHARSET=utf8;
INSERT INTO t_account(id, user_id, total, used, residue) VALUES(1,1,1000,0,1000);
随意填充相关数据。
2、分别向数据库中导入db_undo_log.sql
db_undo_log.sql
文件位置在seata
安装目录中的conf/db_undo_log.sql
。
这个sql为回滚记录表
。
-- the table to store seata xid data
-- 0.7.0+ add context
-- you must to init this sql for you business databese. the seata server not need it.
-- 此脚本必须初始化在你当前的业务数据库中,用于AT 模式XID记录。与server端无关(注:业务数据库)
-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
-- drop table `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
操作完成后的图示如下所示:
3、实现逻辑业务编码
创建两个(及以上)微服务,由于业务逻辑同时跨越两个数据库,其中又有两次rpc远程调用,具备分布式事务测试前提。
下订单–>减库存–>扣余额–>改(订单)状态
创建cloudalibaba-Seata-order-9001
、cloudalibaba-Seata-account-9002
、cloudalibaba-Seata-storage-9003
项目。
配置项,以
cloudalibaba-Seata-order-9001
为例做代码介绍,其他配置项可以参考博客下方github链接查看。
- 依赖引入
<?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>SpringCloud-Alibaba-Demo</artifactId>
<groupId>cn.linkpower</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-Seata-order-9001</artifactId>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--seata依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<!--由于此处使用的是0.9.0版本的seata,需要移除高版本pom中默认嵌入的seata-->
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--单独引入0.9.0版本的seata-->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>0.9.0</version>
</dependency>
<!--openfeign引入-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
<scope>provided</scope>
</dependency>
<!--热部署-->
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency> -->
</dependencies>
</project>
- 配置文件编写
开放指定端口信息,注册nacos、匹配seata等
## 该服务的端口信息
server:
port: 9001
## mybatis 配置
mybatis:
## 驼峰命名匹配
configuration:
map-underscore-to-camel-case: true
type-aliases-package: cn.linkpower.dao
## 扫描mapper文件
mapper-locations:
- classpath:mybatis/orderMapper/**/*.xml
## 服务别名和nacos服务注册发现地址配置
spring:
application:
name: seata-order-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 ##注册至nacos
alibaba:
seata:
# 与seata配置文件中 config/file.config中配置的service相同
tx-service-group: xiangjiao_tx_group
datasource:
url: jdbc:mysql://192.168.99.100:3306/seata_order?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
## 监控相关
management.endpoints.web.exposure.include: '*'
# 禁用hystrix,因为cloud alibaba有sentinel
feign:
hystrix:
enabled: false
尤其需要注意:
- 增加
resources/file.conf
文件(微服务下)
在resources
目录下创建file.conf
文件,并拷贝
seata安装目录下conf/file.conf
中的内容。
但需要修改变更其中的小配置。具体变更如下图所示:(整体文件,请参考github中的代码)
- 增加
resources/registry.conf
配置文件(微服务下)
在resources
目录下创建registry.conf
文件,并拷贝
seata安装目录下conf/file.conf
中的内容。
具体查看文章末尾给出的项目github-demo。 - 编写实体类、service、dao、mapper等
业务的分析,本次只写明seata-order-service
服务部分。
当前的业务需求为:
创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
此时创建需要使用到的数据库操作语句:
import cn.linkpower.entity.Order;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface OrderDao
{
//1 新建订单
void create(Order order);
//2 修改订单状态,从零改为1
void update(@Param("userId") Long userId,@Param("status") Integer status);
}
其中xml部分如下所示:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.linkpower.dao.OrderDao">
<!--映射关系-->
<resultMap id="BaseResultMap" type="cn.linkpower.entity.Order">
<!--主键-->
<id column="id" property="id" jdbcType="BIGINT"/>
<!--其他参数对应列-->
<result column="user_id" property="userId" jdbcType="BIGINT"/>
<result column="product_id" property="productId" jdbcType="BIGINT"/>
<result column="count" property="count" jdbcType="INTEGER"/>
<result column="money" property="money" jdbcType="DECIMAL"/>
<result column="status" property="status" jdbcType="INTEGER"/>
</resultMap>
<insert id="create">
insert into t_order (id,user_id,product_id,count,money,status)
values (null,#{userId},#{productId},#{count},#{money},0);
</insert>
<update id="update">
update t_order set status = 1
where user_id=#{userId} and status = #{status};
</update>
</mapper>
创建OrderService 接口和实现类
。
import cn.linkpower.entity.Order;
public interface OrderService
{
void create(Order order);
}
其具体实现类如下所示:
import cn.linkpower.dao.OrderDao;
import cn.linkpower.entity.Order;
import cn.linkpower.service.AccountService;
import cn.linkpower.service.OrderService;
import cn.linkpower.service.StorageService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
@Slf4j
public class OrderServiceImpl implements OrderService
{
@Resource
private OrderDao orderDao;
@Resource
private StorageService storageService;
@Resource
private AccountService accountService;
/**
* 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
* 简单说:下订单->扣库存->减余额->改状态
*/
@Override
// name属性必须唯一!
@GlobalTransactional(name = "create-order",rollbackFor = Exception.class)
public void create(Order order)
{
log.info("----->开始新建订单");
//1 新建订单
orderDao.create(order);
//2 扣减库存
log.info("----->订单微服务开始调用库存,做扣减Count");
storageService.decrease(order.getProductId(),order.getCount());
log.info("----->订单微服务开始调用库存,做扣减end");
//3 扣减账户
log.info("----->订单微服务开始调用账户,做扣减Money");
accountService.decrease(order.getUserId(),order.getMoney());
log.info("----->订单微服务开始调用账户,做扣减end");
//4 修改订单状态,从零到1,1代表已经完成
log.info("----->修改订单状态开始");
orderDao.update(order.getUserId(),0);
log.info("----->修改订单状态结束");
log.info("----->下订单结束了,O(∩_∩)O哈哈~");
}
}
由于使用分布式开发的策略,其他的如库存微服务
、用户微服务
需要使用到feign
实现聚合微服务
。
建立AccountService.java
和StorageService.java
,具体代码如下所示:
import cn.linkpower.entity.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "seata-storage-service")
public interface StorageService
{
@PostMapping(value = "/storage/decrease")
CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}
import cn.linkpower.entity.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.math.BigDecimal;
@FeignClient(value = "seata-account-service")
public interface AccountService
{
@PostMapping(value = "/account/decrease")
CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
}
- 创建项目启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients //使用激活feign
@EnableDiscoveryClient
public class SeataOrserApplication {
public static void main(String[] args) {
SpringApplication.run(SeataOrserApplication.class);
}
}
*具体代码请参考文章末尾git项目源码
- 启动
cloudalibaba-Seata-order-9001
查看编码是否报错
注意:
一定要先启动 nacos ,再启动 seata ,最后再启动项目。
另外的项目代码配置等和其类似,此处不做重复说明。
全局事务测试
- 数据库的初始状态数据
测试之前,检查数据库中的初始数据信息
seata_account
:
seata_order
:
seata_storage
:
- 正常测试
按照seata官方说明,在主聚合微服务的业务层中,加上@GlobalTransactional
注解,即可实现分布式事务。
正常请求接口,此时接口中未加入相关异常代码。
http://localhost:9001/order/create?userId=1&productId=1&count=10&money=100
请求后,查看控制台日志输出情况。
此时数据库中的数据信息为:
seata_account
:
seata_order
:
seata_storage
:
- 异常测试
设定账户操作超时。修改其中业务逻辑,如下所示:
import cn.linkpower.dao.AccountDao;
import cn.linkpower.service.AccountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;
@Service
public class AccountServiceImpl implements AccountService {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class);
@Resource
AccountDao accountDao;
/**
* 扣减账户余额
*/
@Override
public void decrease(Long userId, BigDecimal money) {
LOGGER.info("------->account-service中扣减账户余额开始");
//模拟超时异常,全局事务回滚
//暂停几秒钟线程 -- 设置数据处理超时20秒
try { TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
accountDao.decrease(userId,money);
LOGGER.info("------->account-service中扣减账户余额结束");
}
}
在此请求上述接口测试:
http://localhost:9001/order/create?userId=1&productId=1&count=10&money=100
同时查看数据库:
seata_account
:
seata_order
:
seata_storage
:
测试结果:
添加了 @GlobalTransactional 注解,居然
没回滚??
问题解决:
究其原因,依旧还是使用的事务为本地事务,而非分布式事务。
如何才能更好的开启分布式事务,而不是使用本地事务呢?
需要关闭本地数据源自动配置,采取手动事务配置!
具体操作:
增加数据源加载、事务配置逻辑,关闭自动配置逻辑。
import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import javax.sql.DataSource;
/**
* 配置数据源,使用seata对数据源做代理
*/
@Configuration
public class DataSourcesConfig {
@Value("${mybatis.mapperLocations}")
private String mapperLocations;
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
/**
* 使用 io.seata.rm.datasource.DataSourceProxy
* @param druidDataSource
* @return
*/
@Bean
public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSourceProxy);
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
bean.setMapperLocations(resolver.getResources(mapperLocations));
return bean.getObject();
}
}
启动类增加配置项,去除自动数据源配置项,采取手动配置:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
//移除自动数据源配置,采取自定义数据源配置方式
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableFeignClients //使用激活feign
@EnableDiscoveryClient
public class SeataOrserApplication {
public static void main(String[] args) {
SpringApplication.run(SeataOrserApplication.class);
}
}
增加
@Value("${mybatis.mapperLocations}")
可能会出现获取失败
的现象,如果出现报错信息,请修改application.yml
中的mybatis.mapperLocations: classpath:mapper/*.xml
。
修改另外几个微服务的配置项,重启项目,将数据库数据恢复至异常前,再次测试:
2020-12-23 18:12:31.000 INFO 12240 --- [nio-9001-exec-1] i.s.common.loader.EnhancedServiceLoader : load TransactionManager[null] extension by class[io.seata.tm.DefaultTransactionManager]
2020-12-23 18:12:31.000 INFO 12240 --- [nio-9001-exec-1] io.seata.tm.TransactionManagerHolder : TransactionManager Singleton io.seata.tm.DefaultTransactionManager@636a50cb
2020-12-23 18:12:31.007 INFO 12240 --- [nio-9001-exec-1] i.s.common.loader.EnhancedServiceLoader : load LoadBalance[null] extension by class[io.seata.discovery.loadbalance.RandomLoadBalance]
2020-12-23 18:12:31.022 INFO 12240 --- [nio-9001-exec-1] i.seata.tm.api.DefaultGlobalTransaction : Begin new global transaction [192.168.99.106:8091:2062440398]
2020-12-23 18:12:31.027 INFO 12240 --- [nio-9001-exec-1] c.l.service.impl.OrderServiceImpl : ----->开始新建订单
2020-12-23 18:12:31.281 WARN 12240 --- [nio-9001-exec-1] i.s.common.loader.EnhancedServiceLoader : load [io.seata.rm.datasource.undo.parser.ProtostuffUndoLogParser] class fail. io/protostuff/runtime/RuntimeEnv
2020-12-23 18:12:31.283 INFO 12240 --- [nio-9001-exec-1] i.s.common.loader.EnhancedServiceLoader : load UndoLogParser[jackson] extension by class[io.seata.rm.datasource.undo.parser.JacksonUndoLogParser]
2020-12-23 18:12:31.339 INFO 12240 --- [nio-9001-exec-1] c.l.service.impl.OrderServiceImpl : ----->订单微服务开始调用库存,做扣减Count
2020-12-23 18:12:32.279 INFO 12240 --- [nio-9001-exec-1] c.netflix.config.ChainedDynamicProperty : Flipping property: seata-storage-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-12-23 18:12:32.322 INFO 12240 --- [nio-9001-exec-1] c.netflix.loadbalancer.BaseLoadBalancer : Client: seata-storage-service instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=seata-storage-service,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2020-12-23 18:12:32.332 INFO 12240 --- [nio-9001-exec-1] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater
2020-12-23 18:12:32.623 INFO 12240 --- [nio-9001-exec-1] c.netflix.config.ChainedDynamicProperty : Flipping property: seata-storage-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-12-23 18:12:32.626 INFO 12240 --- [nio-9001-exec-1] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client seata-storage-service initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=seata-storage-service,current list of Servers=[192.168.99.106:9003],Load balancer stats=Zone stats: {unknown=[Zone:unknown; Instance count:1; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:192.168.99.106:9003; Zone:UNKNOWN; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:com.alibaba.cloud.nacos.ribbon.NacosServerList@71ba8ff0
2020-12-23 18:12:33.159 INFO 12240 --- [nio-9001-exec-1] c.l.service.impl.OrderServiceImpl : ----->订单微服务开始调用库存,做扣减end
2020-12-23 18:12:33.160 INFO 12240 --- [nio-9001-exec-1] c.l.service.impl.OrderServiceImpl : ----->订单微服务开始调用账户,做扣减Money
2020-12-23 18:12:33.203 INFO 12240 --- [nio-9001-exec-1] c.netflix.config.ChainedDynamicProperty : Flipping property: seata-account-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-12-23 18:12:33.205 INFO 12240 --- [nio-9001-exec-1] c.netflix.loadbalancer.BaseLoadBalancer : Client: seata-account-service instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=seata-account-service,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2020-12-23 18:12:33.208 INFO 12240 --- [nio-9001-exec-1] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater
2020-12-23 18:12:33.214 INFO 12240 --- [nio-9001-exec-1] c.netflix.config.ChainedDynamicProperty : Flipping property: seata-account-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-12-23 18:12:33.215 INFO 12240 --- [nio-9001-exec-1] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client seata-account-service initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=seata-account-service,current list of Servers=[192.168.99.106:9002],Load balancer stats=Zone stats: {unknown=[Zone:unknown; Instance count:1; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:192.168.99.106:9002; Zone:UNKNOWN; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:com.alibaba.cloud.nacos.ribbon.NacosServerList@7235740a
2020-12-23 18:12:33.337 INFO 12240 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: seata-storage-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-12-23 18:12:34.209 INFO 12240 --- [erListUpdater-1] c.netflix.config.ChainedDynamicProperty : Flipping property: seata-account-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-12-23 18:12:34.351 INFO 12240 --- [atch_RMROLE_1_8] i.s.core.rpc.netty.RmMessageListener : onMessage:xid=192.168.99.106:8091:2062440398,branchId=2062440400,branchType=AT,resourceId=jdbc:mysql://192.168.99.100:3306/seata_order,applicationData=null
2020-12-23 18:12:34.353 INFO 12240 --- [atch_RMROLE_1_8] io.seata.rm.AbstractRMHandler : Branch Rollbacking: 192.168.99.106:8091:2062440398 2062440400 jdbc:mysql://192.168.99.100:3306/seata_order
2020-12-23 18:12:34.396 INFO 12240 --- [atch_RMROLE_1_8] i.s.r.d.undo.AbstractUndoLogManager : xid 192.168.99.106:8091:2062440398 branch 2062440400, undo_log deleted with GlobalFinished
2020-12-23 18:12:34.397 INFO 12240 --- [atch_RMROLE_1_8] io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_Rollbacked
2020-12-23 18:12:34.416 INFO 12240 --- [nio-9001-exec-1] i.seata.tm.api.DefaultGlobalTransaction : [192.168.99.106:8091:2062440398] rollback status:Rollbacked
2020-12-23 18:12:34.436 ERROR 12240 --- [nio-9001-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.RetryableException: Read timed out executing POST http://seata-account-service/account/decrease?userId=1&money=100] with root cause
java.net.SocketTimeoutException: Read timed out
查看数据库,发现数据再未出现上述问题!ok~解决。
测试出现的问题汇总
- seata出现
no available server to connect
出现这样的原因需要检测两个配置点:
1、registry.conf配置文件中,nacos注册地址是否写错。
2、注意启动时间顺序,先启动nacos,在启动seata
。
3、一定要使用Seata的事务
,关闭Spring自带的本地自动数据源事务配置!
github代码参考地址
cloudalibaba-Seata-order-9001
cloudalibaba-Seata-account-9002
cloudalibaba-Seata-storage-9003