20-SpringCloudAlibaba-3

七 分布式事物处理

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-bank2cloud:nacos:discovery:# Nacos服务地址server-addr: 114.117.183.67:8848datasource:url: jdbc:mysql://114.117.183.67:3306/bank2?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTCusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver

创建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("account_info")
public class AccountInfo {//id@TableIdprivate 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 {@Autowiredprivate AccountMapper accountMapper;@Overridepublic 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 {@Autowiredprivate 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-bank1cloud:nacos:discovery:# Nacos服务地址server-addr: 114.117.183.67:8848datasource:url: jdbc:mysql://114.117.183.67:3306/bank1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTCusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver

创建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("account_info")
public class AccountInfo {//id@TableIdprivate 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 {@Autowiredprivate AccountMapper accountMapper;@AutowiredBank2ServiceFeign bank2ServiceFeign;@Overridepublic 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 {@Autowiredprivate 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: fileservice:# seata服务端的地址和端口信息,多个使用英文分号分隔grouplist:default: 114.117.183.67:9999tx-service-group: my_test_tx_group

bank1微服务开启全局事物

    @Transactional@GlobalTransactional//开启全局事务@Overridepublic 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@Overridepublic 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.xdnf.cn/news/140144.html

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

相关文章

红黑树Java实现

文章目录 红黑树1. 概念性质2. 红黑树节点定义3. 红黑树的插入情况1情况2情况3其它细节问题插入代码实现 4. 红黑树的验证5.性能分析 红黑树 1. 概念性质 红黑树也是一种二插搜索树&#xff0c;每一个节点上比普通二插搜索树都增加了一个存储位置表示节点的颜色&#xff0c;可…

Spring面试题15:Spring支持几种bean的作用域?singleton、prototype、request的区别是什么?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring支持几种bean的作用域? Spring支持以下几种Bean的作用域: Singleton(单例):这是Spring默认的作用域。使用@Scope(“singleton”)注解或…

pyspark常用算子总结

欢迎关注微信公众号&#xff0c;更多优质内容会在微信公众号首发 1. pyspark中时间格式的数据转换为字符串格式的时间&#xff0c;示例代码 from datetime import datetimedate_obj datetime(2023, 7, 2) formatted_date date_obj.strftime("%Y-%m-%d %H:%M:%S")p…

MySQL基础—从零开始学习MySQL

01.MySQL课程介绍_哔哩哔哩_bilibili 1、MySQL安装 以管理员身份运行cmd net start mysql80net stop mysql80 客户端连接 1). 方式一&#xff1a;使用MySQL提供的客户端命令行工具 2). 方式二&#xff1a;使用系统自带的命令行工具执行指令 mysql [-h 127.0.0.1] [-P 3…

C++真的是 C加加

&#x1f4dd;个人主页&#xff1a;夏目浅石. &#x1f4cc;博客专栏&#xff1a;C的故事 &#x1f3e0;学习社区&#xff1a;夏目友人帐. 文章目录 前言Ⅰ. 函数重载0x00 重载规则0x01 函数重载的原理名字修饰 Ⅱ. 引用0x00 引用的概念0x01 引用和指针区分0x03 引用的本质0x04…

SpringBoot-JWT生成

一、理论 1.配置pom.xml <!-- JWT令牌--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency> 2.加密方式 说明:官网JSON Web Tokens - jwt…

Java实验案例(一)

目录 案例一&#xff1a;买飞机票 案例二&#xff1a;开发验证码 案例三&#xff1a;评委打分 案例四&#xff1a;数字加密 案例五&#xff1a;数组拷贝 案例六&#xff1a;抢红包 案例七&#xff1a;找素数的三种方法 案例八&#xff1a;打印乘法口诀表 案例九&#x…

Python项目Flask ipv6双栈支持改造

一、背景 Flask 是一个微型的(轻量)使用Python 语言开发的 WSGI Web 框架(一组库和模块),基于Werkzeug WSGI工具箱/库和Jinja2 模板引擎,当然,Python的WEB框架还有:Django、Tornado、Webpy,这暂且不提。 Flask使用BSD授权。 Flask也被称为microframework(微框架),F…

【UE 粒子练习】02——使用一些常用的模块来创建粒子

目录 效果 步骤 一、创建材质 二、创建粒子 2.1 必需模块 2.2 初始大小模块 2.3 初始位置模块 2.4 初始速度模块 2.5 生命周期模块 2.6 加速-》恒加速度模块 2.7 生成模块 2.8 生命内颜色模块 2.9 尺寸-》大小随速度模块 2.10 碰撞-》Actor碰撞模块 2.1…

stack与queue的简单封装

前言&#xff1a; stack与queue即栈和队列&#xff0c;先进后出/先进先出的特性我们早已了然于心&#xff0c; 在学习数据结构时&#xff0c;我们利用c语言实现栈与队列&#xff0c;从结构体写起&#xff0c;利用数组或指针表示他们的数据成员&#xff0c;之后再一个个实现他们…

(避开网上复制操作)最详细的树莓派刷机配置(含IP固定、更改国内源的避坑操作、SSH网络登录、VNC远程桌面登录)

一、准备工作 SD卡格式化 二、 树莓派系统环境搭建&#xff08;官方&#xff09; 官方镜像 1.1、 必备的配件 读卡器&#xff0c; 内存卡&#xff08;强烈推荐 32GB 内存卡&#xff0c; #lite 命令行界面版本至少需要 8G&#xff0c; 图形化带桌面版镜像需要 16GB&#xf…

笔试强训Day(一)

T1&#xff1a;组队竞赛 链接&#xff1a;组队竞赛__牛客网 牛牛举办了一次编程比赛,参加比赛的有3*n个选手,每个选手都有一个水平值a_i.现在要将这些选手进行组队,一共组成n个队伍,即每个队伍3人.牛牛发现队伍的水平值等于该队伍队员中第二高水平值。 例如: 一个队伍三个队员…

自己写过比较蠢的代码:从失败中学习的经验

文章目录 引言1. 代码没有注释2. 长函数和复杂逻辑3. 不恰当的变量名4. 重复的代码5. 不适当的异常处理6. 硬编码的敏感信息7. 没有单元测试结论 &#x1f389; 自己写过比较蠢的代码&#xff1a;从失败中学习的经验 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&a…

Postgresql事务测试

参考一个事务中 可以查询自己未提交的数据吗_最详细MySQL事务隔离级别及原理讲解&#xff01;&#xff08;二&#xff09;-CSDN博客 一个事务中 可以查询自己未提交的数据吗_趣说数据库事务隔离级别与原理_weixin_39747293的博客-CSDN博客 【MySql&#xff1a;当前读与快照读…

eNSP基础网络学习-v02

一、eNSP 1.什么是eNSP eNSP(Enterprise Network Simulation Platform)是一款由华为提供的免费的、可扩展的、图形化操作的网络仿真工具平台&#xff0c;主要对企业网络路由器、交换机进行软件仿真&#xff0c;完美呈现真实设备实景&#xff0c;支持大型网络模拟&#xff0c;让…

stm32之看门狗

STM32 有两个看门狗&#xff0c;独立看门狗和窗口看门狗&#xff0c;独立看门狗又称宠物狗&#xff0c;窗 口看门狗又称警犬。可用来检测和解决由软件错误引起的故障。两个看门狗的原理都是当计数器达到给定的超时值时&#xff0c;产生系统复位&#xff0c;对于窗口型看门狗同…

VMware中安装Ubuntu(2023年)

Ubuntu安装 前言 安装过程中电脑发热时正常的&#xff0c;这个还是稍微有点点大&#xff1b;下载的版本根据自己的喜好来&#xff0c;新版本肯定要比旧版本占用的空间更大&#xff0c;大家自行选择&#xff1b;仅供学习使用的话可以下载成熟稳定的版本&#xff0c;例如16、18…

《Kubernetes部署篇:Ubuntu20.04基于containerd部署kubernetes1.25.14集群(多主多从)》

一、架构图 如下图所示: 二、环境信息 1、资源下载基于containerd部署容器版kubernetes1.25.14集群资源合集 2、部署规划主机名K8S版本系统版本内核版本IP地址备注k8s-master-121.25.14Ubuntu 20.04.5 LTS5.15.0-69-generic192.168.1.12master节点 + etcd节点k8s-master-131.…

摸鱼也摸鱼之在线数独自动求解

背景 在发现被老板CPU之后&#xff0c;大家想做的基本上都是摸鱼&#xff0c;像我这种没什么手法的人不可能摸鱼打MOBA游戏&#xff0c;所以只能选择数独这种对时间要求不怎么急促的小游戏。然而&#xff0c;有时候搞半天才发现从一开始就错了&#xff0c;这让我很苦恼&#x…