Skip to content

Latest commit

 

History

History
186 lines (142 loc) · 4.88 KB

File metadata and controls

186 lines (142 loc) · 4.88 KB

Spring Cloud 使用 Seata 实现分布式事务 - JPA

使用 Seata 作为分布式事务组件,配置中心和注册中心使用 Nacos,使用 MySQL 数据库,使用 JPA 作为数据访问层

环境准备

创建数据库及表

  • 业务表
DROP DATABASE IF EXISTS seata;
CREATE DATABASE seata;
USE seata;

CREATE TABLE account
(
    id               INT AUTO_INCREMENT PRIMARY KEY,
    balance          INT,
    last_update_time DATETIME DEFAULT now() ON UPDATE now()
);

CREATE TABLE product
(
    id               INT AUTO_INCREMENT PRIMARY KEY,
    price            INT,
    stock            INT,
    last_update_time DATETIME DEFAULT now() ON UPDATE now()
);

CREATE TABLE orders
(
    id               INT AUTO_INCREMENT PRIMARY KEY,
    user_id          INT,
    product_id       INT,
    pay_amount       INT,
    status           VARCHAR(100),
    add_time         DATETIME DEFAULT now(),
    last_update_time DATETIME DEFAULT now() ON UPDATE now()
);

INSERT INTO account (id, balance)
VALUES (1, 10);
INSERT INTO product (id, price, stock)
VALUES (1, 5, 10);

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;

启动 Nacos

docker run --name nacos -p 8848:8848 -e MODE=standalone nacos/nacos-server

启动 Seata Server

可以直接通过bash 脚本启动 Seata Server,也可以通过 Docker 镜像启动,但是 Docker 方式目前只支持使用 file 的模式,不支持将 Seata-Server 注册到 Eureka 或 Nacos 等注册中心

  • 通过脚本启动

Seata Release 下载相应版本的 Seata Server,解压后执行以下命令启动,这里使用 file 配置

sh ./bin/seata-server.sh -p 8091 -m file
  • 通过 Docker 启动
docker run --name seata-file -d -p 8091:8091 hellowoodes/seata:0.7.1-file

测试

  • 启动应用

分别启动 Order-Service,Pay-Service 和 Storage-Service

  • 测试成功场景

调用 placeOrder 接口,将 price 设置为 1,此时余额为 10,可以下单成功

curl -X POST \
  http://localhost:8081/order/placeOrder \
  -H 'Content-Type: application/json' \
  -d '{
    "userId": 1,
    "productId": 1,
    "price": 1
}'

此时返回结果为:

{"success":true,"message":null,"data":null}
  • 测试失败场景

设置 price 为 100,此时余额不足,会下单失败,pay-service会抛出异常,事务会回滚

curl -X POST \
  http://localhost:8081/order/placeOrder \
  -H 'Content-Type: application/json' \
  -d '{
    "userId": 1,
    "productId": 1,
    "price": 100
}'

查看 undo_log 的日志或者主键,可以看到在执行过程中有保存数据

如查看主键自增的值,在执行前后的值会发生变化,在执行前是 1,执行后是 5

SELECT
    auto_increment
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'seata'
  AND TABLE_NAME = 'undo_log'

注意

DataSourceProxy 配置

这里是尤其需要注意的,Seata 是通过代理数据源实现事务分支,所以需要配置 io.seata.rm.datasource.DataSourceProxy 的 Bean,且是 @Primary默认的数据源,否则事务不会回滚,无法实现分布式事务

@Configuration
public class DataSourceProxyConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Primary
    @Bean
    public DataSourceProxy dataSource(DruidDataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }
}

如果使用的是 Hikari 数据源,需要修改数据源的配置,以及注入的 Bean 的配置前缀

spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.jdbc-url=jdbc:mysql://localhost:3306/seata?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.hikari.username=root
spring.datasource.hikari.password=123456
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public DataSource dataSource() {
        return new HikariDataSource();
    }

版本

需要注意的是当前Spring Boot 和 Spring Cloud 以及 Spring Cloud Alibaba 的版本号需要互相对应,否则可能会存在各种问题;具体可以参考版本说明