当前位置: 首页 > news >正文

SpringCloudAlibaba Seata分布式事务

分布式事务

事务是数据库的概念,数据库事务(ACID:原子性、一致性、隔离性和持久性);

分布式事务的产生,是由于数据库的拆分和分布式架构(微服务)带来的,在常规情况下,我们在一个进程中操作一个数据库,这属于本地事务,如果在一个进程(java程序)中操作多个数据库,或者在多个进程中操作一个或多个数据库,就产生了分布式事务;

(1)数据库分库分表就产生了分布式事务;

在这里插入图片描述

(2)项目拆分服务化也产生了分布式事务;

在这里插入图片描述

Seata介绍

Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务;

Seata为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案;

目前使用的流行度情况是:AT > TCC > Saga > XA;XA的流行度是我自己编的。。。

我们可以参看seata各公司使用列表:
https://github.com/seata/seata/issues/1246 大部分公司都采用的AT事务模式;

Seata已经在国内很多团队开始落地,其中不乏有大公司;

Github:https://github.com/seata/seata
官网:http://seata.io/
当前最新版本:1.3.0

Seata架构

在Seata的架构中,一共有三个角色:

在这里插入图片描述

  • TC (Transaction Coordinator) - 事务协调者

维护全局和分支事务的状态,驱动全局事务提交或回滚;

  • TM (Transaction Manager) - 事务管理器

定义全局事务的范围:开始全局事务、提交或回滚全局事务;

  • RM (Resource Manager) - 资源管理器

管理分支事务处理的资源,与TC交互以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚;

其中TC为单独部署的 Server 服务端,TM和RM为嵌入到应用中的 Client 客户端;

在Seata中,一个分布式事务的生命周期如下:

在这里插入图片描述

  1. TM请求TC开启一个全局事务,TC会生成一个XID作为该全局事务的编号,XID会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起;

  2. RM请求TC将本地事务注册为全局事务的分支事务,通过全局事务的XID进行关联;

  3. TM请求TC告诉XID对应的全局事务是进行提交还是回滚;

  4. TC驱动RM将XID对应的自己的本地事务进行提交还是回滚;

TC就是事务管理器,RM就是每个数据库连接(或者说本地事务),TM就是事务的入口(或者说事务的发起方),当然,它本身也是一个RM

AT模式事务案例

单体应用多数据源分布式事务

在这里插入图片描述

在Spring Boot单体项目中,如果使用了多数据源,就需要考虑多个数据源的数据一致性,即产生了分布式事务的问题,我们采用Seata的AT事务模式来解决该分布式事务问题;

以电商购物下单为例:
在这里插入图片描述

  1. 准备数据库表和数据;

其中每个库中的undo_log表,是 Seata AT模式必须创建的表,主要用于分支事务的回滚;

  1. 开发一个SpringBoot单体应用

注意:以下代码并没有自己跑通过,是视频老师的

  • dynamic-datasource-spring-boot-starter 多数据源
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.bjpowernode</groupId><artifactId>29-seata-distributed-transaction</artifactId><version>1.0.0</version><name>29-seata-distributed-transaction</name><description>29-seata-distributed-transaction project for Spring Boot</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.0.RELEASE</spring-boot.version><spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!-- mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- mybatis-spring-boot-starter --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><!-- seata-spring-boot-starter --><dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>1.3.0</version></dependency><!--dynamic-datasource-spring-boot-starter动态数据源mybatis-plus的作者写的这个作用:在一个项目中可以连接多个数据库(多数据源)--><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.2.0</version></dependency><!-- nacos-client --><dependency><groupId>com.alibaba.nacos</groupId><artifactId>nacos-client</artifactId><version>1.3.1</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><!--mybatis代码自动生成插件--><plugin><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.4.0</version><configuration><!--配置文件的位置--><configurationFile>src/main/resources/generatorConfig.xml</configurationFile><!--生成代码过程中是否打印日志--><verbose>true</verbose><!--生成时是否覆盖java文件,xml文件总是合并--><overwrite>true</overwrite></configuration></plugin></plugins><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource><resource><directory>src/main/resources</directory><includes><include>**/*.*</include></includes></resource></resources></build></project>
# 服务端口号
server.port=8080# 应用服务名称
spring.application.name=29-seata-distributed-transaction# 设置默认的数据源或者数据源组,default master
spring.datasource.dynamic.primary=order-ds# order数据源配置
spring.datasource.dynamic.datasource.order-ds.url=jdbc:mysql://39.99.163.122:3306/orderdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
spring.datasource.dynamic.datasource.order-ds.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.order-ds.username=mysql
spring.datasource.dynamic.datasource.order-ds.password=UoT1R8[09/VsfXoO5>6YteB# product数据源配置
spring.datasource.dynamic.datasource.product-ds.url=jdbc:mysql://39.99.163.122:3306/productdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
spring.datasource.dynamic.datasource.product-ds.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.product-ds.username=mysql
spring.datasource.dynamic.datasource.product-ds.password=UoT1R8[09/VsfXoO5>6YteB# account数据源配置
spring.datasource.dynamic.datasource.account-ds.url=jdbc:mysql://39.99.163.122:3306/accountdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
spring.datasource.dynamic.datasource.account-ds.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.account-ds.username=mysql
spring.datasource.dynamic.datasource.account-ds.password=UoT1R8[09/VsfXoO5>6YteB# 是否启动对seata的集成
spring.datasource.dynamic.seata=true# Seata应用编号 default ${spring.application.name}
seata.application-id=${spring.application.name}
# Seata事务组编号,用于TC集群名
seata.tx-service-group=${spring.application.name}-group
# 虚拟组和分组的映射
# 29-seata-distributed-transaction-group 对应的就是 Seata事务组编号
#   也可以尝试用 ${seata.tx-service-group} 来获取
seata.service.vgroup-mapping.29-seata-distributed-transaction-group=default
# 这个default 对应的就是 上面那条的 default
seata.service.grouplist.default=192.168.172.128:8091
  • @DS(value = “order-ds”)

动态切换数据源就是依靠这个注解

  • @GlobalTransactional,事务的入口

还可以用在类上,就相当于给类上的每一个方法都加上了@GlobalTransactional

  • @Transactional,事务的分支,这个不用加也可以?有待考证 todo
package com.bjpowernode.service.impl;import com.baomidou.dynamic.datasource.annotation.DS;
import com.bjpowernode.mapper.OrdersMapper;
import com.bjpowernode.model.Orders;
import com.bjpowernode.model.Product;
import com.bjpowernode.service.AccountService;
import com.bjpowernode.service.OrderService;
import com.bjpowernode.service.ProductService;
import io.seata.core.context.RootContext;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.math.BigDecimal;@Slf4j
@Service
public class OrderServiceImpl implements OrderService {@Autowiredprivate OrdersMapper ordersMapper;@Autowiredprivate AccountService accountService;@Autowiredprivate ProductService productService;@Override@DS(value = "order-ds")@GlobalTransactional //seata全局事务注解public Integer createOrder(Integer userId, Integer productId) throws Exception {Integer amount = 1; // 购买数量暂时设置为 1log.info("当前 XID: {}", RootContext.getXID());// 减库存Product product = productService.reduceStock(productId, amount);// 减余额accountService.reduceBalance(userId, product.getPrice());// 下订单Orders order = new Orders();order.setUserId(userId);order.setProductId(productId);order.setPayAmount(product.getPrice().multiply(new BigDecimal(amount)));ordersMapper.insertSelective(order);log.info("下订单: {}", order.getId());// 返回订单编号return order.getId();}
}package com.bjpowernode.service.impl;import com.baomidou.dynamic.datasource.annotation.DS;
import com.bjpowernode.mapper.ProductMapper;
import com.bjpowernode.model.Product;
import com.bjpowernode.service.ProductService;
import io.seata.core.context.RootContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Slf4j
@Service
public class ProductServiceImpl implements ProductService {@Autowiredprivate ProductMapper productMapper;@Override@DS(value = "product-ds")@Transactionalpublic Product reduceStock(Integer productId, Integer amount) throws Exception {log.info("当前 XID: {}", RootContext.getXID());// 检查库存Product product = productMapper.selectByPrimaryKey(productId);if (product.getStock() < amount) {throw new Exception("库存不足");}// 扣减库存int updateCount = productMapper.reduceStock(productId, amount);// 扣除成功if (updateCount == 0) {throw new Exception("库存不足");}// 扣除成功log.info("扣除 {} 库存成功", productId);return product;}
}package com.bjpowernode.service.impl;import com.baomidou.dynamic.datasource.annotation.DS;
import com.bjpowernode.mapper.AccountMapper;
import com.bjpowernode.model.Account;
import com.bjpowernode.service.AccountService;
import io.seata.core.context.RootContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.math.BigDecimal;@Slf4j
@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountMapper accountMapper;@Override@DS(value = "account-ds")@Transactionalpublic void reduceBalance(Integer userId, BigDecimal money) throws Exception {log.info("当前 XID: {}", RootContext.getXID());// 检查余额Account account = accountMapper.selectAccountByUserId(userId);if (account.getBalance().doubleValue() < money.doubleValue()) {throw new Exception("余额不足");}// 扣除余额int updateCount = accountMapper.reduceBalance(userId, money);// 扣除成功if (updateCount == 0) {throw new Exception("余额不足");}log.info("扣除用户 {} 余额成功", userId);}
}

测试:http://localhost:8080/order?userId=1&productId=1

微服务的分布式事务

在这里插入图片描述

  • Cloud项目用的依赖和单体项目用的依赖不一样

Cloud用spring-cloud-starter-alibaba-seata

单体项目用seata-spring-boot-starter

  • 配置文件Seata的部分,与单体项目的配置一样,每个微服务都需要配置
  • 使用方面也与单体项目是一样的
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.bjpowernode</groupId><artifactId>30-seata-tcc-order-service</artifactId><version>1.0.0</version><name>30-seata-tcc-order-service</name><description>30-seata-tcc-order-service project for Spring Boot</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.0.RELEASE</spring-boot.version><spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--spring-cloud-starter-alibaba-sentinel--><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-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!-- mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- mybatis-spring-boot-starter --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><!-- spring-cloud-starter-alibaba-seata --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions><exclusion><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId></exclusion></exclusions></dependency><!--统一通用项目,model类、feign接口--><dependency><groupId>com.bjpowernode</groupId><artifactId>30-seata-tcc-commons</artifactId><version>1.0.0</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency><!-- spring-cloud-dependencies --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR3</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource><resource><directory>src/main/resources</directory><includes><include>**/*.*</include></includes></resource></resources></build></project>

AT事务模式分布式事务工作机制

前提
基于支持本地 ACID 事务的关系型数据库;(mysql、oracle)

Java 应用,通过JDBC访问数据库;

整体机制
就是两阶段提交协议的演变:

  • 一阶段:

“业务数据“和“回滚日志记录“在同一个本地事务中提交,释放本地锁和连接资源;

  • 二阶段:

如果没有异常异步化提交,非常快速地完成;
如果有异常回滚通过一阶段的回滚日志进行反向补偿;

具体举例说明整个AT分支的工作过程

业务表:product

Field	Type	    Key
id	    bigint(20)	PRI
name	varchar(100)	
since	varchar(100)	

AT分支事务的业务逻辑:

update product set name = 'GTS' where name = 'TXC';

一阶段过程

  1. 解析SQL,得到SQL的类型(UPDATE),表(product),条件(where name = ‘TXC’)等相关的信息;

  2. 查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据;
    select id, name, since from product where name = ‘TXC’;
    得到前镜像:

id	name	since
1	TXC	    2014
  1. 执行业务 SQL:更新这条记录的 name 为 ‘GTS’;
  2. 查询后镜像:根据前镜像的结果,通过 主键 定位数据;
select id, name, since from product where id = 1;

得到后镜像:

id	name	since
1	GTS	    2014
  1. 插入回滚日志:把前后镜像数据以及业务SQL相关的信息组成一条回滚日志记录,插入到 UNDO_LOG 表中;

  2. 分支事务提交前,向TC注册分支,申请product表中,主键值等于1的记录的全局锁(在当前的同一个全局事务id范围内是可以申请到全局锁的,不同的全局事务id才会排斥);

  3. 本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交;

  4. 将本地事务提交的结果上报给TC;

二阶段-回滚

  1. 收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作;
  2. 通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录;
  3. 数据校验:拿 UNDO LOG 中的后镜像与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改,这种情况,需要人工来处理;
  4. 根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:
update product set name = 'TXC' where id = 1;
  1. 提交本地事务,并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC;

二阶段-提交

  1. 收到TC的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给TC;
  2. 异步任务阶段的分支提交请求将异步和批量地删除相应UNDO LOG记录;

回滚日志表:

Field	        Type
branch_id	    bigint       PK
xid	            varchar(100)
context	        varchar(128)
rollback_info	longblob
log_status	    tinyint
log_created	    datetime
log_modified	datetime
SQL建表语句:
CREATE TABLE `undo_log` (`id` bigint NOT NULL AUTO_INCREMENT,`branch_id` bigint NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

写隔离

多线程模式下的一阶段和二阶段是怎么保证数据的一致性的问题的?

一阶段本地事务提交前,需要确保先拿到全局锁(暂时理解为就相当于是个分布式锁)
拿不到 全局锁 ,不能提交本地事务;
拿 全局锁 的尝试被限制在一定范围内(10次),超出范围将放弃,并回滚本地事务,释放本地锁;

以一个示例来说明:

两个或者多个全局事务 tx1 和 tx2,分别并发对 a 表的 m 字段进行更新操作,m 的初始值 1000;

假设tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900,本地事务提交前,先拿到该记录的 全局锁 ,拿到了全局锁,本地提交并释放本地锁;

tx2后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800,本地事务提交前,尝试拿该记录的 全局锁 ,tx1全局提交前,该记录的全局锁一直会被 tx1 持有,tx2 需要重试等待 全局锁 ;

todo 这个图我看的有点儿迷,先放这儿再说吧
在这里插入图片描述

tx1 二阶段全局提交,释放 全局锁 ,tx2 拿到 全局锁 提交本地事务;
在这里插入图片描述

  • 这里的本地锁就是行锁,这里默认两个事务操作的是同一条数据

如果 tx1 的二阶段全局回滚,则 tx1 需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚;
此时,如果 tx2 仍在等待该数据的 全局锁,同时持有本地锁,则 tx1 的分支回滚会失败。分支的回滚会一直重试,直到 tx2 的 全局锁 等锁超时,放弃 全局锁 并回滚本地事务释放本地锁,tx1 的分支回滚最终成功;

因为整个过程 全局锁 在 tx1 结束前一直是被 tx1 持有的,所以不会发生脏写的问题;

读隔离

在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted);表示有可能读到未提交的数据(脏读)

如果应用在特定场景下,必需要求全局的 读已提交 ,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理;

SELECT FOR UPDATE 语句的执行会申请 全局锁 ,如果 全局锁 被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试,这个过程中,查询是被 block 住的,直到 全局锁 拿到,即读取的相关数据是 已提交 的,才返回;

如果不加 FOR UPDATE是不需要获取全局锁就可以读到的。

出于总体性能上的考虑,Seata目前的方案并没有对所有SELECT语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句;

TC Server运行环境部署

单机版

我们先部署单机环境的 Seata TC Server,用于学习或测试,在生产环境中要部署集群环境;

因为TC需要进行全局事务和分支事务的记录,所以需要对应的存储,目前,TC有三种存储模式( store.mode ):

  • file模式:适合单机模式,全局事务会话信息在内存中读写,并持久化本地文件 root.data,性能较高;
  • db模式:适合集群模式,全局事务会话信息通过 db 共享,相对性能差点;
  • redis模式:解决db存储的性能问题;

我们先采用file模式,最终我们部署单机TC Server如下图所示:

在这里插入图片描述

Seata运行环境部署
下载Seata:http://seata.io/zh-cn/blog/download.html
解压:tar -zxvf seata-server-1.3.0.tar.gz
切换cd seata

在这里插入图片描述

默认seata-server.sh脚本设置的jvm内存参数2G,我们再虚拟机里面做实验,可以改小一点;

在这里插入图片描述

在bin目录下启动:./seata-server.sh
默认配置下,Seata TC Server 启动在 8091 端口;
因为我们没有修改任何配置文件,默认情况seata使用的是file模式进行数据持久化,所以可以看到用于持久化的本地文件 root.data;

集群版

生产环境下,需要部署集群 Seata TC Server,实现高可用,在集群时,多个 Seata TC Server 通过 db 数据库或者redis实现全局事务会话信息的共享;

每个Seata TC Server注册自己到注册中心上,应用从注册中心获得Seata TC Server实例,这就是Seata TC Server的集群;
在这里插入图片描述

Seata TC Server 对主流的注册中心都提供了集成,Naco作为注册中心越来越流行,这里我们就采用Nacos;

Seata TC Server集群搭建具体步骤
1、下载并解压两个seata-server-1.3.0.tar.gz;
2、初始化 Seata TC Server 的 db 数据库,在 MySQL 中,创建 seata 数据库,并在该库下执行如下SQL脚本:
使用seata-1.3.0\script\server\db脚本(网盘有共享)
3、修改 seata/conf/file.conf 配置文件,修改使用 db 数据库,实现 Seata TC Server 的全局事务会话信息的共享;
(1)mode = “db”
(2)数据库的连接信息
driverClassName = “com.mysql.cj.jdbc.Driver”
url = “jdbc:mysql://39.99.163.122:3306/seata”
user = “mysql”
password = “UoT1R8[09/VsfXoO5>6YteB”
4、设置使用 Nacos 注册中心;
修改 seata/conf/registry.conf 配置文件,设置使用 Nacos 注册中心;
(1)、type = “nacos”
(2)Nacos连接信息:
nacos {
application = “seata-server”
serverAddr = “127.0.0.1:8848”
group = “SEATA_GROUP”
namespace = “”
cluster = “default”
username = “”
password = “”
}
5、启动数据库和nacos;
6、启动两个 TC Server
执行 ./seata-server.sh -p 18091 -n 1 命令,启动第一个TC Server;

  • -p:Seata TC Server 监听的端口;
  • -n:Server node,在多个 TC Server 时,需区分各自节点,用于生成不同区间的 transactionId 事务编号,以免冲突;

执行 ./seata-server.sh -p 28091 -n 2 命令,启动第二个TC Server;
7、打开Nacos注册中心控制台,可以看到有两个Seata TC Server 实例;
8、应用测试;

在这里插入图片描述

测试

对于SpringBoot单体应用
1、添加nacos客户端依赖;

<!-- nacos-client -->
<dependency><groupId>com.alibaba.nacos</groupId><artifactId>nacos-client</artifactId><version>1.3.1</version>
</dependency>

2、配置application.properties文件

# Seata应用编号,默认为${spring.application.name}
seata.application-id=springcloud-order-seata
# Seata事务组编号,用于TC集群名
seata.tx-service-group=springcloud-order-seata-group
# 虚拟组和分组的映射
seata.service.vgroup-mapping.springcloud-order-seata-group=default#seata-spring-boot-starter 1.1版本少一些配置项
seata.enabled=true
seata.registry.type=nacos
seata.registry.nacos.cluster=default
seata.registry.nacos.server-addr=192.168.172.128:8848
# nacos中Seata服务的分组
seata.registry.nacos.group=SEATA_GROUP
# nacos中Seata服务的名字
seata.registry.nacos.application=seata-server

在这里插入图片描述

在这里插入图片描述

微服务

对于Spring Cloud Alibaba微服务应用:
则不需要加nacos的jar包依赖,application.properties文件配置完全一样;

部分内容转发自:
https://www.bilibili.com/video/BV1vy4y167Dc/?p=219&spm_id_from=pageDriver&vd_source=64c73c596c59837e620fed47fa27ada7

华为公司一直贯彻选拔制,因为人才不是靠培养,而是自我成长,我们要创造人才成长的土壤,就如“一杯咖啡吸收宇宙能量”。我们不要给高级专家担负太多管理人才的责任,不要搞“拉郎配”,要给他们自由度,他有多大能量就发挥多大能量。

任正非


http://www.mrgr.cn/news/6241.html

相关文章:

  • sysctl 命令设置内核参数
  • 无心剑中译莎士比亚《吾爱稀罕胜天仙》
  • 分贝通解读:减少不必要的支出浪费,企业真的能省钱
  • 牛客小白月赛99 ABCDE (fg挺典的,明天补)
  • Ansible远程自动化运维
  • 【C++】stack和queue
  • PaddleNLP 3.0 支持大语言模型开发
  • 推荐一款好用的mac解压缩软件
  • 产品经理-​​实习中的自我迭代(41)
  • 关于shell输出颜色的事情
  • PCIe学习笔记(27)
  • 关于我的生信笔记开通《知识星球》
  • 【TCP】确认应答、超时重传机制和TCP报头
  • 如何使用ssm实现游戏攻略网站的设计与实现+vue
  • 学历不高能进大厂么?
  • c++自定义迭代器,如跳表,怎么实现
  • 流媒体协议之RTCP
  • 使用kubeadm快速部署一套K8S集群
  • Verilog刷题笔记58
  • 根据状态的不同,显示不同的背景颜色