20-SpringCloudAlibaba-3

news2024/9/27 6:29:33

七 分布式事物处理

1 认识本地事物

在这里插入图片描述

什么是事物

事务就是针对数据库的一组操作,它可以由一条或多条SQL语句组成,同一个事务的操作具备同步的特点,事务中的语句要么都执行,要么都不执行

举个栗子:

你去小卖铺买东西,一手交钱,一手交货就是一个事务的例子,交钱和交货必须全部成功,事务才算成功,任何一个活动失败,事务将撤销所有已成功的活动。

什么是本地事物

在计算机系统中,更多的是通过关系型数据库来控制事务,这是利用数据库本身的事务特性来实现的,因此叫数据库事务,由于应用主要靠关系数据库来控制事务,而数据库通常和应用在同一个服务器,所以基于关系型数据库的事务又被称为本地事务。

在这里插入图片描述

解释:

  • Business∶我们具体的业务代码
  • Storage∶ 库存业务代码;扣库存
  • Order∶订单业务代码;保存订单
  • Account∶账号业务代码;减账户余额

数据库事务的四大特性ACID

在这里插入图片描述

  • 原子性(Atomicity)
    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  • 一致性(Consistency)
    事务前后数据的完整性必须保持一致。
  • 隔离性(Isolation)
    事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
  • 持久性(Durability)
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

总结

数据库事务在实现时会将一次事务涉及的所有操作全部纳入到一个不可分割的执行单元,该执行单元中的所有操作要么都成功,要么都失败,只要其中任一操作执行失败,都将导致整个事务的回滚。

2 认识分布式事物

在这里插入图片描述

前言

随着互联网的快速发展,软件系统由原来的单体应用转变为分布式应用,下图描述了单体应用向微服务的演变。

在这里插入图片描述

注意:

分布式系统会把一个应用系统拆分为可独立部署的多个服务, 因此需要服务与服务之间远程协作才能完成事务操作,这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务,例如用户注册送积分事务、创建订单减库存事务,银行转账事务等都是分布式事务。

假如没有分布式事务

在一系列微服务系统当中,假如不存在分布式事务,会发生什么呢?让我们以互联网中常用的交易业务为例子:

在这里插入图片描述

解释:

上图中包含了库存和订单两个独立的微服务,每个微服务维护了自己的数据库。在交易系统的业务逻辑中,一个商品在下单之前需要先调用库存服务,进行扣除库存,再调用订单服务, 创建订单记录。

正常情况下,两个数据库各自更新成功,两边数据维持着一致性。

在这里插入图片描述

但是,在非正常情况下,有可能库存的扣减完成了,随后的订单记录却因为某些原因插入失败。这个时候,两边数据就失去了应有的一致性。

在这里插入图片描述

问题:

这种时候需要要保证数据的一致性,单数据源的一致性靠单机事物来保证,多数据源的一致性就要靠分布式事物保证。

什么是分布式事务

指一次大的操作由不同的小操作组成的,这些小的操作分布在不同的服务器上,分布式事务需要保证这些小操作要么全部成功,要么全部失败。从本质上来说,分布式事务就是为了保证不同数据库的数据一致性

3 分布式事务产生的场景

在这里插入图片描述

跨JVM进程

当我们将单体项目拆分为分布式、微服务项目之后,各个服务之间通过远程REST或者RPC调用来协同完成业务操作。

在这里插入图片描述

典型的场景:

商城系统中的订单微服务和库存微服务,用户在下单时会访问订单微服务,订单微服务在生成订单记录时,会调用库存微服务来扣减库存。各个微服务是部署在不同的JVM进程中的,此时,就会产生因跨JVM进程而导致的分布式事务问题。

跨数据库实例

单体系统访问多个数据库实例,也就是跨数据源访问时会产生分布式事务。

在这里插入图片描述

典型的场景:

例如,我们的系统中的订单数据库和交易数据库是放在不同的数据库实例中,当用户发起退款时,会同时操作用户的订单数据库和交易数据库,在交易数据库中执行退款操作,在订单数据库中将订单的状态变更为已退款。由于数据分布在不同的数据库实例,需要通过不同的数据库连接会话来操作数据库中的数据,此时,就产生了分布式事务。

多个服务一个数据库

多个微服务访问同一个数据库。

在这里插入图片描述

典型场景:

例如,订单微服务和库存微服务访问同一个数据库也会产生分布式事务,原因是:多个微服务访问同一个数据库,本质上也是通过不同的数据库会话来操作数据库,此时就会产生分布式事务。

4 什么是两阶段提交

在这里插入图片描述

两阶段提交又称2PC,2PC是一个非常经典的强一致、中心化的原子提交协议。

这里所说的中心化是指协议中有两类节点:一个是中心化协调者节点 (coordinator)和 N个参与者节点 (partcipant)。

两个阶段:第一阶段:投票阶段 和第二阶段:提交/执行阶段

2pc例子

A组织B、C和D三个人去爬山:如果所有人都同意去爬山,那么活动将举行;如果有一人不同意去爬山,那么活动将取消。

在这里插入图片描述

首先A将成为该活动的协调者,B、C和D将成为该活动的参与者。

在这里插入图片描述

具体流程:

  • 阶段1:

    ①A发邮件给B、C和D,提出下周三去爬山,问是否同意。 那么此时A需要等待B、C和D的邮件。

    ②B、C和D分别查看自己的日程安排表。B、C发现自己在当日没有活动安排,则发邮件告诉A它们同意下周三去爬山。由于某种原因, D白天没有查看邮件。那么此时A、B和C均需要等待。到晚上的时候,D发现了A的邮件,然后查看日程安排, 发现周三当天已经有别的安排,那么D回复A说活动取消吧。

  • 阶段2:

    ①此时A收到了所有活动参与者的邮件,并且A发现D下周三不能去爬山。那么A将发邮件通知B、C和D,下周三爬山活动取消。

    ②此时B、C回复A“太可惜了”,D回复A“不好意思”。至此该事务终止。

2PC阶段处理流程

举例订单服务A,需要调用支付服务B去支付,支付成功则处理购物订单为待发货状态,否则就需要将购物订单处理为失败状态。

第一阶段:投票阶段

在这里插入图片描述

第一阶段分三步:

  • 事物询问:协调者向所有的参与者发送事务预处理请求,称之为Prepare,并开始等待各参与者的响应。
  • 执行本地事物:各个参与者节点执行本地事务操作,但在执行完成后并不会真正提交数据库本地事务,而是先向协调者报告说:“我这边可以处理了/我这边不能处理”。.
  • 各个参与者向协调者反馈事物询问的响应:如果所有参与者成功执行了事务操作,那么就反馈给协调者Yes响应,表示事务可以执行;如果有参与者执行事务失败,那么就反馈给协调者No响应, 表示事务不可以执行。第一阶段执行完后,会有两种可能。1、所有都返回Yes. 2、有一个或者多个返回No。

第二阶段:提交/执行阶段(成功流程)

成功条件 :所有参与者都返回Yes。

在这里插入图片描述

异常流程第二阶段也分为两步

  • 发送回滚请求

    协调者向所有参与者节点发出 RoollBack 请求

  • 事务回滚

    参与者接收到RoollBack请求后,会回滚本地事务。

2PC缺点

  • 性能问题

    无论是在第一阶段的过程中,还是在第二阶段,所有的参与者资源和协调者资源都是被锁住的,只有当所有节点准备完毕,事务协调者才会通知进行全局提交,参与者进行本地事务提交后才会释放资源。这样的过程会比较漫长,对性能影响比较大。

  • 单节点故障

    由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。

5 XA方案

在这里插入图片描述

什么是DTP

2PC的传统方案是在数据库层面实现的,如Oracle、MySQL都支持 2PC协议,为了统一标准减少行业内不必要的对接成本,需要制定标准化的处理模型及接口标准,国际开放标准组织Open Group定义分布式事务处理模型DTP(Distributed Transaction Processing Reference Model)。

DTP模型定义角色

  • AP(Application Program):即应用程序,可以理解为使用DTP分布式事务的程序。
  • RM(Resource Manager):即资源管理器,可以理解为事务的参与者,一般情况下是指一个数据库实例,通过资源管理器对该数据库进行控制,资源管理器控制着分支事务。
  • TM(Transaction Manager):事务管理器,负责协调和管理事务,事务管理器控制着全局事务,管理事务生命周期,并协调各个RM。全局事务是指分布式事务处理环境中,需要操作多个数据库共同完成一个工作,这个工作即是一个全局事务。

注意:

DTP模型定义TM和RM之间通讯的接口规范叫XA,简单理解为数据库提供的2PC接口协议,基于数据库的XA协议来实现2PC又称为XA方案。

在这里插入图片描述

执行流程:

  1. 应用程序持有用户库和积分库两个数据源。
  2. 应用程序通过TM通知用户库RM新增用户,同时通知积分库RM为该用户新增积分,RM此时并未提交事物,此时用户和积分资源锁定。
  3. TM收到回复,只要有一方失败则分别向其他RM发起回滚事物,回滚完毕,资源释放。
  4. TM收到执行回复,全部成功,此时向所有RM发起提交事物,提交完毕,资源锁释放。

6 Seata方案

在这里插入图片描述

Seata是什么

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 为用户提供了 ATTCCSAGAXA 事务模式,为用户打造一站式的分布式解决方案。

Seata整体框架

全局事务与分支事务的关系图

在这里插入图片描述

与传统2PC的模型类似,Seata定义了三个组件来协议分布式事务的处理过程

在这里插入图片描述

具体流程:

  • Transaction Coordinator(TC):事务协调者,它是独立的中间件,需要独立部署运行,它维护全局事务的运行状态,接收TM指令发起全局事务的提交与回滚,负责与RM通信协调各个分支事务的提交或回滚。
  • Transaction Manager(TM):事务管理器,TM需要嵌入应用程序中工作,它负责开启一个全局事务,并最终向TC发起全局提交或全局回滚的指令。
  • Resource Manager(RM):资源管理器,控制分支事务,负责分支注册、状态汇报,并接收事务协调器 TC的指令,驱动分支(本地)事务的提交和回滚。

还拿新用户注册送积分举例Seata的分布式事务过程

在这里插入图片描述

执行流程 :

  1. 用户服务的TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID。
  2. 用户服务的RM向TC注册分支事务,该分支事务在用户服务执行新增用户逻辑,并将其纳入 XID对应全局事务的管辖。
  3. 用户服务执行分支事务,向用户表插入一条记录。
  4. 逻辑执行到远程调用积分服务时(XID在微服务调用链路的上下文中传播)。积分服务的RM 向TC注册分支事务,该分支事务执行增加积分的逻辑,并将其纳入XID对应全局事务的管辖。
  5. 积分服务执行分支事务,向积分记录表插入一条记录,执行完毕后,返回用户服务。
  6. 用户服务分支事务执行完毕。
  7. TM向TC发起针对XID的全局提交或回滚决议。
  8. TC调度XID下管辖的全部分支事务完成提交或回滚请求。

Seata实现2PC与传统2PC的差别

  • 架构层次方面,传统2PC方案的RM实际上是在数据库层,RM本质上就是数据库自身,通过XA协议实现,而Seata的RM是以jar包的形式作为中间件层部署在应用程序的这一侧的。
  • 性能层面:两阶段提交方面,传统2PC无论第二阶段的决议是commit还是rollbcak,事务性资源的锁都要保持到Phase2完成才释放。而Seata的做法是在Phase1就将本地事务提交,这样就可以省去Phase2持锁的时间,整体提高效率。

八 Seata提供XA模式实现分布式事务

1 业务说明

在这里插入图片描述

业务说明

本实例通过Seata中间件实现分布式事务,模拟两个账户的转账交易过程。两个账户在两个不同的银行(张三在bank1、李四在 bank2),bank1和bank2是两个微服务。交易过程中,张三给李四转账制定金额。上述交易步骤,要么一起成功,要么一起失败,必须是一个整体性的事务。

在这里插入图片描述

工程环境

在这里插入图片描述

创建数据库

Docker安装Mysql

查看镜像

docker search mysql:5.7

下载镜像

docker pull mysql:5.7

启动镜像

docker run --name mysql -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

参数:

-e 配置环境变量 MYSQL_ROOT_PASSWORD 设置容器内mysql root 密码

bank1库,包含张三账户

CREATE DATABASE /*!32312 IF NOT
EXISTS*/
`bank1` /*!40100 DEFAULT CHARACTER
SET utf8 */;

USE `bank1`;

/*Table structure for table `account_info`
*/
DROP TABLE
IF EXISTS `account_info`;

CREATE TABLE `account_info` (
	`id` BIGINT (20) NOT NULL AUTO_INCREMENT,
	`account_name` VARCHAR (100) COLLATE utf8_bin DEFAULT NULL COMMENT '户主姓名',
	`account_no` VARCHAR (100) COLLATE utf8_bin DEFAULT NULL COMMENT '银行卡号',
	`account_password` VARCHAR (100) COLLATE utf8_bin DEFAULT NULL COMMENT '帐户密码',
	`account_balance` DOUBLE DEFAULT NULL COMMENT '帐户余额',
	PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 3 DEFAULT CHARSET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;

/*Data for the table `account_info` */
INSERT INTO `account_info` (
	`id`,
	`account_name`,
	`account_no`,
	`account_password`,
	`account_balance`
)
VALUES
	(1, '张三', '1', NULL, 1000);

bank2库,包含李四账户

CREATE DATABASE /*!32312 IF NOT
EXISTS*/
`bank2` /*!40100 DEFAULT CHARACTER
SET utf8 */;

USE `bank2`;

/*Table structure for table `account_info`
*/
DROP TABLE
IF EXISTS `account_info`;

CREATE TABLE `account_info` (
	`id` BIGINT (20) NOT NULL AUTO_INCREMENT,
	`account_name` VARCHAR (100) COLLATE utf8_bin DEFAULT NULL COMMENT '户主姓名',
	`account_no` VARCHAR (100) COLLATE utf8_bin DEFAULT NULL COMMENT '银行卡号',
	`account_password` VARCHAR (100) COLLATE utf8_bin DEFAULT NULL COMMENT '帐户密码',
	`account_balance` DOUBLE DEFAULT NULL COMMENT '帐户余额',
	PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 4 DEFAULT CHARSET = utf8 COLLATE = utf8_bin ROW_FORMAT = DYNAMIC;

/*Data for the table `account_info` */
INSERT INTO `account_info` (
	`id`,
	`account_name`,
	`account_no`,
	`account_password`,
	`account_balance`
)
VALUES
	(2, '李四', '2', NULL, 0);

2 下载启动Seata服务

在这里插入图片描述

下载seata服务器

下载地址 :https://github.com/seata/seata/releases

在这里插入图片描述

解压并启动

tar -zxvf seata-server-1.4.2.tar.gz -C /usr/local/
#后台运行
nohup sh seata-server.sh -p 9999 -h 114.117.183.67 -m file &> seata.log &

注意:

其中9999为服务端口号;file为启动模式,这里指seata服务将采用文件的方式存储信息。

测试

查看启动日志

cat seata.log

3 转账功能实现-上

实现如下功能

李四账户增加金额。

创建cloud-seata-bank2

pom引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

编写主启动类

//添加对mapper包扫描 Mybatis-plus
@MapperScan("com.lxx.mapper")
//开启发现注册
@EnableDiscoveryClient
@SpringBootApplication
@Slf4j
public class SeataBank2Main6002 {
    public static void main(String[] args) {

        SpringApplication.run(SeataBank2Main6002.class, args);
        log.info("************** SeataBank1Main6002 *************");
    }
}

编写YML配置文件

server:
  port: 6002
spring:
  application:
    name: provider-bank2
  cloud:
    nacos:
      discovery:
        # Nacos服务地址
        server-addr: 114.117.183.67:8848
  datasource:
    url: jdbc:mysql://114.117.183.67:3306/bank2?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

创建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("account_info")
public class AccountInfo {
    //id
    @TableId
    private Long id;
    //户主姓名
    @TableField("account_name")
    private String accountName;
    //银行卡号
    @TableField("account_no")
    private String accountNo;
    //账户密码
    @TableField("account_password")
    private String accountPassword;
    //账户余额
    @TableField("account_balance")
    private Double accountBalance;
}

编写持久层

@Mapper
public interface AccountMapper extends BaseMapper<AccountInfo> {

}

编写转账接口

public interface IAccountInfoService {

    //李四增加金额
    void transfer(String accountNo, Double amount);
}

编写转账接口实现类

@Service
public class AccountInfoServiceImpl implements IAccountInfoService {

    @Autowired
    private AccountMapper accountMapper;

    @Override
    public void transfer(String accountNo, Double amount) {
        // 1.获取用户信息
        QueryWrapper<AccountInfo> queryWrapper = new QueryWrapper();
        //注意:构造时使用的是数据库字段,不是entity属性
        queryWrapper.eq("account_no", accountNo);
        AccountInfo accountInfo = accountMapper.selectOne(queryWrapper);
        // 2.判断accountInfo是否为空
        if (accountInfo != null) {
            // 3.给李四加钱
            accountInfo.setAccountBalance(accountInfo.getAccountBalance() + amount);
            accountMapper.updateById(accountInfo);
        }

    }
}

编写控制层

@RestController
@RequestMapping("/bank2")
public class Bank2Controller {

    @Autowired
    private IAccountInfoService accountInfoService;

    //李四接收张三的转账
    @GetMapping("/transfer")
    public String transfer(String accountNo, Double amount) {
        //李四增加金额
        accountInfoService.transfer(accountNo, amount);
        return "bank2" + amount;
    }

}

4 转账功能实现-下

实现如下功能

1、张三账户减少金额

2、远程调用bank2向李四转账。

创建cloud-seata-bank1

在这里插入图片描述

pom引入依赖

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

编写主启动类

//添加对mapper包扫描 Mybatis-plus
@MapperScan("com.lxx.mapper")
//开启OpenFiegn
@EnableFeignClients
//开启发现注册
@EnableDiscoveryClient
@SpringBootApplication
@Slf4j
public class SeataBank1Main6001 {
    public static void main(String[] args) {
        SpringApplication.run(SeataBank1Main6001.class, args);
        log.info("************** SeataBank1Main6001 *************");
    }

}

编写YML配置文件

server:
  port: 6001
spring:
  application:
    name: consumer-bank1
  cloud:
    nacos:
      discovery:
        # Nacos服务地址
        server-addr: 114.117.183.67:8848
  datasource:
    url: jdbc:mysql://114.117.183.67:3306/bank1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

创建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("account_info")
public class AccountInfo {
    //id
    @TableId
    private Long id;
    //户主姓名
    @TableField("account_name")
    private String accountName;
    //银行卡号
    @TableField("account_no")
    private String accountNo;
    //账户密码
    @TableField("account_password")
    private String accountPassword;
    //账户余额
    @TableField("account_balance")
    private Double accountBalance;
}

编写持久层

@Mapper
public interface AccountMapper extends BaseMapper<AccountInfo> {

}

编写转账接口

public interface IAccountInfoService {

    //张三扣减金额
    void transfer(String accountNo, Double amount);
}

编写远程调用接口

package com.lxx.feign;

@FeignClient(value = "provider-bank2")
public interface Bank2ServiceFeign {

    @GetMapping("/bank2/transfer")
    String transfer(@RequestParam("accountNo") String accountNo, @RequestParam("amount") Double amount);

}

编写转账接口实现类

@Service
public class AccountInfoServiceImpl implements IAccountInfoService {

    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    Bank2ServiceFeign bank2ServiceFeign;

    @Override
    public void transfer(String accountNo, Double amount) {
        // 1.获取用户信息
        QueryWrapper<AccountInfo> queryWrapper = new QueryWrapper();
        //注意:构造时使用的是数据库字段,不是entity属性
        queryWrapper.eq("account_no", accountNo);
        AccountInfo accountInfo = accountMapper.selectOne(queryWrapper);
        // 2.判断accountInfo是否为空
        if (accountInfo != null) {
            // 3.给张三减钱
            accountInfo.setAccountBalance(accountInfo.getAccountBalance() - amount);
            accountMapper.updateById(accountInfo);
            // 4.调用李四微服务,转账,李四增加金额
            bank2ServiceFeign.transfer("2", amount);
        }

    }
}

编写控制层

@RestController
@RequestMapping("/bank1")
public class Bank1Controller {

    @Autowired
    private IAccountInfoService accountInfoService;

    //张三给李四转账
    @GetMapping("/transfer")
    public String transfer(String accountNo, Double amount) {
        //张三减少金额
        //李四增加金额
        accountInfoService.transfer(accountNo, amount);
        return "bank1" + amount;
    }

}

5 没有引入分布式事物问题演示

初始数据库数据

在这里插入图片描述

正常情况

发送请求 http://localhost:6001/bank1/transfer?accountNo=1&amount=100

在这里插入图片描述

制造异常

在bank2微服务制造异常(比如关闭bank2此微服务)

异常后测试

发送请求 http://localhost:6001/bank1/transfer?accountNo=1&amount=100

在这里插入图片描述

6 项目引入Seata

在这里插入图片描述

Seata实现XA要点

1、全局事务开始使用@GlobalTransactional标识。

2、每个本地事务方案仍然使用@Transactional标识。

3、每个数据库都需要创建undo_log表,此表是Seata保证本地事务一致性的关键。

创建 UNDO_LOG 表

SEATA XA 模式需要 UNDO_LOG 表

-- 注意此处0.3.0+ 增加唯一索引 ux_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;

添加依赖

        <!-- 分布式事务Seata  -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>

修改配置文件YML

seata:
  # 注册中心
  registry:
    type: file
  service:
    # seata服务端的地址和端口信息,多个使用英文分号分隔
    grouplist:
      default: 114.117.183.67:9999
  tx-service-group: my_test_tx_group

bank1微服务开启全局事物

    @Transactional
    @GlobalTransactional//开启全局事务
    @Override
    public void transfer(String accountNo, Double amount) {
        // 1.获取用户信息
        QueryWrapper<AccountInfo> queryWrapper = new QueryWrapper();
        //注意:构造时使用的是数据库字段,不是entity属性
        queryWrapper.eq("account_no", accountNo);
        AccountInfo accountInfo = accountMapper.selectOne(queryWrapper);
        // 2.判断accountInfo是否为空
        if (accountInfo != null) {
            // 3.给张三减钱
            accountInfo.setAccountBalance(accountInfo.getAccountBalance() - amount);
            accountMapper.updateById(accountInfo);
            // 4.调用李四微服务,转账,李四增加金额
            bank2ServiceFeign.transfer("2", amount);
        }

    }
}

注意:

将@GlobalTransactional注解标注在全局事务发起的Service实 现方法上,开启全局事务 :GlobalTransactionalInterceptor会 拦截@GlobalTransactional注解的方法,生成全局事务ID (XID),XID会在整个分布式事务中传递。 在远程调用时,spring-cloud-alibaba-seata会拦截Feign调用将 XID传递到下游服务。

bank2微服务开启事物

    @Transactional
    @Override
    public void transfer(String accountNo, Double amount) {
        // 1.获取用户信息
        QueryWrapper<AccountInfo> queryWrapper = new QueryWrapper();
        //注意:构造时使用的是数据库字段,不是entity属性
        queryWrapper.eq("account_no", accountNo);
        AccountInfo accountInfo = accountMapper.selectOne(queryWrapper);
        // 2.判断accountInfo是否为空
        if (accountInfo != null) {
            // 3.给李四加钱
            accountInfo.setAccountBalance(accountInfo.getAccountBalance() + amount);
            accountMapper.updateById(accountInfo);
        }

    }

测试分布式事物

制作bank2异常时,发送请求 http://localhost:6001/bank1/transfer?accountNo=1&amount=100

在这里插入图片描述

总结

传统2PC(基于数据库XA协议)和Seata实现2PC的两种2PC方案, 由于Seata的零入侵并且解决了传统2PC长期锁资源的问题,所以推荐采用Seata实现2PC。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1026161.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《已解决 Bug TypeError: Cannot read property ‘props‘ of undefined (React)》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页: &#x1f405;&#x1f43e;猫头虎的博客&#x1f390;《面试题大全专栏》 &#x1f995; 文章图文并茂&#x1f996…

2023_Spark_实验十一:RDD高级算子操作

//checkpoint &#xff1a;sc.setCheckpointDir("hdfs://Master:9000/ck") // 设置检查点val rdd sc.textFile("hdfs://Master:9000/input/word.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(__) // 执行wordcount任务的转换rdd.checkp…

Windows虚拟机访问网页证书错误问题

问题&#xff1a; 显示证书错误&#xff0c;图片加载不出来&#xff0c;看着很别扭&#xff0c;如下&#xff1a; 方法: 1.先导出可用的证书&#xff1a; 可以将自己正常环境的证书导出来&#xff08;google浏览器为例&#xff09; 浏览器右上角三个竖点——设置——隐私设…

阿里云服务器开放的一个新端口,重启防火墙,端口未启动

问题&#xff1a; 阿里云网页开放的一个新端口后&#xff0c;重启防火墙&#xff0c;端口未启动&#xff0c;之前配置的也都停止了。 解决&#xff1a; 原因可能是阿里的服务控制了&#xff0c;只能一个个端口开启了。把新配置新端口也单独启用。 开启80端口指令 firewall-cm…

mysql启动不了问题

突然昨天早上起来&#xff0c;就发生了这一幕&#xff1a; 启动MySQL服务时出现&#xff02;mysql本地计算机上的MySQL服务启动后停止。某些在未由其他服务或程序使用时将自动停止&#xff02; 几经周折&#xff0c;终于在一个大佬的贴下求得了启动成功的经验&#xff0c;其中我…

Qt---day4---9.20

qt完成时钟&#xff1a; 头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPaintEvent> #include <QtDebug> #include <QPainter> #include <QTimerEvent> #include <QTime>QT_BEGIN_NAMESPACE names…

VirtualBox安装RockyLinux并使用ssh访问

文章目录 1 前言2 安装 Rocky Linux2.1 新建虚拟机2.2 设置虚拟机内存和CPU数量2.3 设置虚拟机硬盘大小2.4 完成设置2.5 启动虚拟机2.6 Rocky Linux 的安装2.6.1 直接回车2.6.2 等待 check 完成2.6.3 设置语言2.6.4 设置最小化安装2.6.5 去除分区设置的感叹号2.6.7 设置 root 账…

Hive 优化建议与策略

目录 ​编辑 一、Hive优化总体思想 二、具体优化措施、策略 2.1 分析问题得手段 2.2 Hive的抓取策略 2.2.1 策略设置 2.2.2 策略对比效果 2.3 Hive本地模式 2.3.1 设置开启Hive本地模式 2.3.2 对比效果 2.3.2.1 开启前 2.3.2.2 开启后 2.4 Hive并行模式 2.5 Hive…

详解C++静态多态和动态多态的区别

目录 1.多态的概念与分类 2.多态的作用 3.静态多态 4.动态多态 5.总结 1.多态的概念与分类 多态&#xff08;Polymorphisn&#xff09;是面向对象程序设计&#xff08;OOP&#xff09;的一个重要特征。多态字面意思为多种状态。在面向对象语言中&#xff0c;一个接口&…

【深度学习实验】前馈神经网络(三):自定义多层感知机(激活函数logistic、线性层算Linear)

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. 构建数据集 2. 激活函数logistic 3. 线性层算子 Linear 4. 两层的前馈神经网络MLP 5. 模型训练 一、实验介绍 本实验实现了一个简单的两层前馈神经网络 激活函数…

一、【漏洞复现系列】Tomcat文件上传 (CVE-2017-12615)

1.1、漏洞原理 描述: Tomcat 是一个小型的轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发和调试JSP 程序的首选。 攻击者将有可能可通过精心构造的攻击请求数据包向服务器上传包含任意代码的 JSP 的webshell文件&#x…

100G QSFP28 100km光模块最新解决方案

随着信息时代的到来&#xff0c;数据传输的速度和距离要求越来越高。目前&#xff0c;易天光通信发布了具有超低成本、可实现100G超长距离传输新方案——100G QSFP28 100km光模块&#xff0c;该方案是在100G ZR4 80km光模块上的全面升级。 一、产品概述 100G ZR4 100km是专为…

requests模块高级用法练习

文章目录 模拟浏览器指纹发送get请求发送post请求文件上传服务器超时 模拟浏览器指纹 打开http://10.9.75.164/php/functions/setcookie.php网页&#xff0c;找到请求头的UA字段&#xff0c;这段信息是浏览器的指纹&#xff08;包括当前系统、浏览器名称和版本&#xff09;&am…

【再识C进阶3(上)】详细地认识字符串函数、进行模拟字符串函数以及拓展内容

小编在写这篇博客时&#xff0c;经过了九一八&#xff0c;回想起了祖国曾经的伤疤&#xff0c;勿忘国耻&#xff0c;振兴中华&#xff01;加油&#xff0c;逐梦少年&#xff01; 前言 &#x1f493;作者简介&#xff1a; 加油&#xff0c;旭杏&#xff0c;目前大二&#xff0c;…

【短文】sambe添加用户时报错Failed to add entry for user

2023年9月20日&#xff0c;周三晚上 Samba fails to add a user entry, how do I fix this? - Ask Ubuntu 也就是说&#xff0c;添加的sambe用户必须是Linux操作系统的用户

2023/09/20 day4 qt

做一个动态指针钟表 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPainter> //绘制事件类 #include <QPaintEvent> //画家类 #include <QTime> #include <QTimer> #include <QTimerEvent> QT_BEGIN…

k8s使用时无法ping通服务器From IP地址 icmp_seq=1 Destination Host Unreachable

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

canvas-绘图库fabric.js简介

一般情况下简单的绘制&#xff0c;其实canvas原生方法也可以满足&#xff0c;比如画个线&#xff0c;绘制个圆形、正方形、加个文案。 let canvas document.getElementById(canvas);canvas.width 1200;canvas.height 600;canvas.style.width 1200px;canvas.style.height 6…

Canal实现Mysql数据同步至Redis、Elasticsearch

文章目录 1.Canal简介1.1 MySQL主备复制原理1.2 canal工作原理 2.开启MySQL Binlog3.安装Canal3.1 下载Canal3.2 修改配置文件3.3 启动和关闭 4.SpringCloud集成Canal4.1 Canal数据结构![在这里插入图片描述](https://img-blog.csdnimg.cn/c64b40c2231a4ea39a95aac81d771bd1.pn…

kafka消费者多线程开发

目录 前言 kafka consumer 设计原理 多线程的方案 参考资料 前言 目前&#xff0c;计算机的硬件条件已经大大改善&#xff0c;即使是在普通的笔记本电脑上&#xff0c;多核都已经是标配了&#xff0c;更不用说专业的服务器了。如果跑在强劲服务器机器上的应用程序依然是单…