SpringCloud Alibaba学习(九)——seata分布式事务(0.9.0)

分布式事务的由来

单体应用,一台服务器上的后台系统,对应一个数据库;
分布式应用,每个单独的应用对应一个独立的数据源,每个服务内部的数据一致性由本地事务保证,但是全局数据一致性无法得到保障!

在这里插入图片描述

例如:

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 官方下载地址

本次学习使用: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-9001cloudalibaba-Seata-account-9002cloudalibaba-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.javaStorageService.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

github代码快速入口

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Spring Cloud Alibaba中使用Seata进行分布式事务,需要进行以下步骤: 1. 引入Seata依赖 在pom.xml文件中引入Seata的依赖: ``` <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <version>2.2.0.RELEASE</version> </dependency> ``` 2. 配置Seata 在application.yml文件中配置Seata的相关参数,包括: - 服务端配置 ``` spring: cloud: alibaba: seata: tx-service-group: my_test_tx_group # Seata事务组名称 service: vgroup-mapping: my_test_tx_group: default # Seata服务组名称 group-id: default # Seata服务分组ID config: type: file # Seata配置类型 file: name: file.conf # Seata配置文件名称 path: /seata/config # Seata配置文件路径 ``` - 客户端配置 ``` mybatis: configuration: # 启用二级缓存 cache-enabled: true # 数据源配置 type-aliases-package: com.example.demo.entity mapper-locations: classpath:mapper/*.xml configuration-properties: # 自动驼峰转换 mapUnderscoreToCamelCase: true # 数据库连接池配置 druid: url: jdbc:mysql://localhost:3306/seata?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # Seata配置 seata: enabled: true # 启用Seata application-id: seata-demo # 应用ID tx-service-group: my_test_tx_group # Seata事务组名称 service: vgroup-mapping: my_test_tx_group: default # Seata服务组名称 # 注册中心配置 registry: type: nacos # 注册中心类型 nacos: server-addr: localhost:8848 # 注册中心地址 namespace: public group: SEATA_GROUP file: name: file.conf # 注册中心配置文件名称 path: /seata/config # 注册中心配置文件路径 ``` 3. 配置数据源 在application.yml文件中配置数据源,包括: ``` spring: datasource: url: jdbc:mysql://localhost:3306/seata?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver ``` 4. 配置Seata代理数据源 在Spring Boot启动类中配置Seata代理数据源: ``` @SpringBootApplication @EnableDiscoveryClient @MapperScan("com.example.demo.mapper") @EnableFeignClients(basePackages = "com.example.demo.feign") @EnableTransactionManagement public class SeataDemoApplication { public static void main(String[] args) { SpringApplication.run(SeataDemoApplication.class, args); } @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource() { return new DruidDataSource(); } @Bean public DataSourceProxy dataSourceProxy(DataSource dataSource) { return new DataSourceProxy(dataSource); } @Bean public GlobalTransactionScanner globalTransactionScanner() { return new GlobalTransactionScanner("seata-demo", "my_test_tx_group"); } } ``` 5. 编写业务代码 在业务代码中使用@GlobalTransactional注解标记需要参与分布式事务的方法,例如: ``` @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Autowired private OrderFeignClient orderFeignClient; @Override @GlobalTransactional public void createOrder(User user) { userMapper.insert(user); orderFeignClient.createOrder(user.getId()); } } ``` 以上就是在Spring Cloud Alibaba中使用Seata进行分布式事务的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值