SpringCloud (4) 分布式事务

分布式事务:在分布式系统中,一个业务需要多个微服务合作完成,每个微服务都有事务,这多个事务必须同时成功或失败

        分支事务:每个微服务的事务

        全局事务:整个业务的事务

1 seata

1.1 seata中3个角色

        TC(Transaction Coordinator)事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚

        TM(Transaction Manager)事务管理器:定义全局事务的范围,开启、提交、回滚全局事务

        RM(Resource Manager)资源管理器:管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态

1.2 部署TC服务

1.2.1 准备数据库表

        新建seata数据库,在数据库中新建一下4个表

-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `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_status_gmt_modified` (`status` , `gmt_modified`),
    KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
    `branch_id`         BIGINT       NOT NULL,
    `xid`               VARCHAR(128) NOT NULL,
    `transaction_id`    BIGINT,
    `resource_group_id` VARCHAR(32),
    `resource_id`       VARCHAR(256),
    `branch_type`       VARCHAR(8),
    `status`            TINYINT,
    `client_id`         VARCHAR(64),
    `application_data`  VARCHAR(2000),
    `gmt_create`        DATETIME(6),
    `gmt_modified`      DATETIME(6),
    PRIMARY KEY (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
    `row_key`        VARCHAR(512) NOT NULL,
    `xid`            VARCHAR(128),
    `transaction_id` BIGINT,
    `branch_id`      BIGINT       NOT NULL,
    `resource_id`    VARCHAR(256),
    `table_name`     VARCHAR(128),
    `pk`             VARCHAR(512),
    `status`         TINYINT      NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
    `gmt_create`     DATETIME,
    `gmt_modified`   DATETIME,
    PRIMARY KEY (`row_key`),
    KEY `idx_status` (`status`),
    KEY `idx_branch_id` (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

CREATE TABLE IF NOT EXISTS `distributed_lock`
(
    `lock_key`       CHAR(20) NOT NULL,
    `lock_value`     VARCHAR(20) NOT NULL,
    `expire`         BIGINT,
    primary key (`lock_key`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);

        在需要接入seata的微服务对应的数据库中新建undo_log表(XA模式不需要此表,AT模式需要此表)

CREATE TABLE `undo_log` (
  `branch_id` BIGINT NOT NULL COMMENT '分支事务ID',
  `xid` VARCHAR(128) NOT NULL COMMENT '全局事务唯一标识',
  `context` VARCHAR(128) NOT NULL COMMENT '上下文',
  `rollback_info` LONGBLOB NOT NULL COMMENT '回滚信息',
  `log_status` INT(11) NOT NULL COMMENT '状态,0正常,1全局已完成(防悬挂)',
  `log_created` DATETIME(6) NOT NULL COMMENT '创建时间',
  `log_modified` DATETIME(6) NOT NULL COMMENT '修改时间',
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8mb4 COMMENT='AT模式回滚日志表';

1.2.2 准备配置文件(application.yml)

sever:
  port: 7099 #web控制台

spring:
  application:
    name: seata-server

logging:
  config: classpath:logback-spring.xml
  file:
    path: ${user.home}/logs/seata

console: #控制台帐号密码
  user:
    username: admin
    password: admin

seata:
  config: #seata配置中心,可以选择file表示直接配置在当前文件(application.yaml)中,也可以选择配置在nacos中
    type: file
#    nacos:
#      server-addr: nacos:8848
#      group: DEFAULT_GROUP
#      namespace: "" #不写默认public
#      dataId: seataServer.properties
#      username: nacos
#      password: nacos
  registry: #seata注册中心,选择nacos
    type: nacos
    nacos:
      application: seata-server
      server-addr: nacos:8848 #nacos地址,可以写成容器名(nacos)+端口,要求nacos和seata部署在同一个docker中,且彼此在同一个网络里; 也可以写成IP地址+端口
      group: DEFAULT_GROUP
      namespace: "" #不写默认public
      username: root
      password: 123456
      #cluster: default #tc集群名称

  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
#如果上面的seata.config.type写的是nacos,就将一下配置写在nacos中;  如果写的是file,就将一下配置写在本application.yml中
  server:
    max-commit-retry-timeout: -1 #二阶段提交重试超时时长(默认-1,表示无限重试)
    max-rollback-retry-timeout: -1 #二阶段回滚重试超时时长(默认-1,表示无限重试)
    rollback-retry-timeout-unlock-enable: false
    enable-check-auth: true
    enable-parallel-request-handle: true
    retry-dead-threshold: 130000
    xaer-nota-retry-timeout: 60000
    enableParallelRequestHandle: true
    recovery:
      committing-retry-period: 1000 #二阶段提交未完成状态全局事务重试提交线程间隔时间(默认1000ms)
      async-committing-retry-period: 1000 #二阶段异步提交状态重试提交线程间隔时间(默认1000ms)
      rollbacking-retry-period: 1000 #二阶段回滚状态重试回滚线程间隔时间(默认1000ms)
      timeout-retry-period: 1000 #超时状态检测重试线程间隔时间(默认1000ms),检测出超时将全局事务置入回滚会话管理器
    undo:
      log-save-days: 7 #undo保留天数(默认7天)
      log-delete-period: 86400000 #undo清理线程间隔时间ms(默认86400000ms)
    session:
      branch-async-queue-size: 5000
      enable-branch-async-remove: false
  store:
    mode: db #也可以存储在file或redis中
    session:
      mode: db
    lock:
      mode: db
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver #mysql8以后的驱动用这个(.cj)
      #此种写法要保证seata与mysql在同一个docker,且在同一个网络中, 否则要写成IP地址(jdbc:mysql://123.123.123.123:3306/seata......)
      url: jdbc:mysql://mysql:3306/seata?rewriteBatchedStatements=true&serverTimezone=UTC
      user: root
      password: 123456
      min-conn: 10 #数据库初始连接数
      max-conn: 100 #数据库最大连接数
      global-table: global_table #全局事务表名
      branch-table: branch_table #分支事务表名
      lock-table: lock_table #全局锁表名
      distributed-lock-table: distributed_lock
      query-limit: 1000 #查询全局事务一次的最大条数(默认100)
      max-wait: 5000 #获取连接时最大等待时间ms(默认5000ms)
  metrics:
    enabled: false
    registry-type: compact
    exporter-list: prometheus
    exporter-prometheus-port: 9898
  transport:
    rpc-tc-request-timeout: 15000
    enable-tc-server-batch-send-response: false
    shutdown:
      wait: 3
    thread-factory:
      boss-thread-prefix: NettyBoss
      worker-thread-prefix: NettyServerNIOWorker
      boss-thread-size: 1

1.2.3 Docker部署

        拉取seata镜像

docker pull seataio/seata-server

        创建seata容器(命令行创建)

docker run --name seata_container \
-p 8099:8099 \
-p 7099:7090 \
--restart=always \
--network sunner_network \
-e SEATA_IP=123.123.123.123 \
-e SEATA_PORT=8099 \
-v /root/docker_seata/application.yml:/seata-server/resources/application.yml \
-v /root/docker_seata/logs:/root/logs \
-d \
seataio/seata-server:latest

        创建seata容器(compose文件创建)

service:
  seata:
    image: seataio/seata-server
    container_name: seata_container
    restart: always  #自动重启
    ports:
      - "8099:8099" #微服务与seata连接
      - "7099:7099" #web控制台
    environment:
      SEATA_IP: 123.123.123.123 #seata所在地址
      SEATA_PORT: 8099
    volumes:
      - "/root/docker_seata/application.yml:/seata-server/resources/application.yml"  #日志
      - "/root/docker_seata/logs:/root/logs"  #项目目录
    networks:
      - sunner_network

2 微服务继承seata

2.1 引入依赖

<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

2.2 微服务配置添加seata

        在nacos中新建shar-seata.yaml文件,用于保存seata配置

seata:
    registry: #注册中心的配置,微服务根据这些信息去注册中心获取TC服务地址
        type: nacos #注册中心类型-nacos
        nacos:
            server-addr: 106.13.228.161:8848
            namespace: "" #不写默认public
            group: DEFAULT_GROUP
            application: seata-server #seata服务名,在nacos注册的服务名
            #username: nacos #nacos帐号(nacos开启鉴权才填)
            #password: nacos #nacos密码(nacos开启鉴权才填)
    tx-service-group: txg #事务组名称(集群)
    service:
        vgroup-mapping: #事务组与TC集群的映射关系
            txg: "default"
            #txg2: "abc"
            #txg3: "bbb"
    data-source-proxy-mode: AT #默认AT模式

        在对应的微服务bootstrp.yaml引入nacos中的seata共享配置share-seata.yaml

spring:
  cloud:
    nacos:
      config:
        file-extension: yaml #文件后缀名/配置格式
        shared-configs: #共享配置
          - data-id: share-seata.yaml

3 XA模式

3.1 XA模式流程

        一阶段:

                ① RM注册分支事务到TC

                ② RM执行分支事务业务sql,但不提交(会锁定数据库)

                ③ RM报告执行状态到TC

        二阶段:

                ① TC检测各分支事务执行状态

                        如果都成功,通知所有RM提交事务

                        如果有失败,通知所有RM回滚事务

                ② RM接收TC命令(提交或回滚)

3.2 XA模式优缺点

        优点:●满足事务的强一致性

                ●实现简单,没有代码侵入

                ●利用数据库机制回滚(常用数据库都支持)

        缺点:●因为一阶段需要锁定数据库,要等二阶段结束才释放,性能较差

                ●依赖关系型数据库

3.3 XA模式的使用

        修改需要使用seata的微服务的bootstrap.yaml(或application.yaml),也可以修改nacos的shar-seata.yaml

seata:
    data-source-proxy-mode: XA #默认AT模式

        在"全局事务"入口的方法上添加@GlobalTransactional注解

        在其他微服务的"分支事务"方法上添加@Transactional注解

4 AT模式(默认)

4.1 AT模式流程

        一阶段:

                ① RM注册分支事务

                ② 记录undo-log(数据快照)

                ③ 执行业务sql并提交

                ④ 报告事务状态

        二阶段:

                RM提交,删除undo-log数据

                RM回滚,根据undo-log恢复数据到更新前

4.2 AT模式优缺点

        优点:●AT模式一阶段直接提交,不锁定资源,性能高

                ●利用数据快照undo-log回滚

        缺点:●不能满足事务强一致性

4.3 AT模式的使用

        修改需要使用seata的微服务的bootstrap.yaml(或application.yaml),也可以修改nacos的shar-seata.yaml

seata:
    data-source-proxy-mode: AT #默认AT模式

        在需要接入seata的微服务对应的数据库中新建undo_log表(XA模式不需要此表,AT模式需要此表)  《见1.2.1》

        在"全局事务"入口的方法上添加@GlobalTransactional注解

        在其他微服务的"分支事务"方法上添加@Transactional注解

4.4 AT模式注意事项

        不能修改主键字段,否则报错

java.sql.SQLException: org.apache.seata.common.exception.ShouldNeverHappenException: Before image size is not equaled to after image size, probably because you updated the primary keys
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值