微服务保护和分布式事务
微服务保护
前面学过了组件Spring Cloud - 韩熙隐ario - 博客园,我们已经可以解决业务中99%的问题了。

我们还可以拥有一些高级的能力,比如:
- 服务保护:业务的优化、系统健壮性的处理和疑难问题的阶段等
- 分布式事务:服务独立,各自操作数据库,如何保证ACID。
服务保护
- 雪崩问题
雪崩问题
保证服务运行的健壮性,避免级联失败导致的雪崩问题,就属于微服务保护。
微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。

比如商品服务出现问题。在购物车积累了很多请求。导致资源耗尽,影响其他服务B的请求。
这样,就造成了雪崩问题

雪崩问题产生的原因是什么?
- 微服务相互调用,服务提供者出现故障或阻塞。
- 服务调用者没有做好异常处理,导致自身故障。
- 调用链中的所有服务级联失败,导致整个集群故障
解决问题的思路有哪些?
- 尽量避免服务出现故障或阻塞。【避免出错】
- 保证代码的健壮性;
- 保证网络畅通;
- 能应对较高的并发请求;
- 服务调用者做好远程调用异常的后备方案,避免故障扩散【减少影响】
服务保护方案
-
请求限流:限制访问微服务的请求的并发量,避免服务因流量激增出现故障。
避免把服务压垮。限流器。但也无法确保服务不出问题

-
线程隔离:也叫做舱壁模式,模拟船舱隔板的防水原理。通过限定每个业务能使用的线程数量而将故障业务隔离,避免故障扩散。
限定每个业务使用的线程数量,将故障隔离。避免故障扩散。减少能影响的数量。

-
快速失败:给业务编写一个调用失败时的处理的逻辑,称为fallback。当调用出现故障(比如无线程可用)时,按照失败处理逻辑执行业务并返回,而不是直接抛出异常。

-
服务熔断:由断路器统计请求的异常比例或慢调用比例,如果超出阈值则会熔断该业务,则拦截该接口的请求。熔断期间,所有请求快速失败,全都走fallback逻辑。
发现异常后,拒绝请求,所有请求快速失败,走fallback【fallback可以说是后续的过程,补充,兜底】。可以返回友好提示等内容。这样就少了远程调用和等待的过程了。

服务保护技术
方案有了,代码要自己写吗?
其实有现成的技术了。主要的是下面两个技术,分别由阿里和网飞开发的
| Sentinel | Hystrix | |
|---|---|---|
| 线程隔离 | 信号量隔离 | 线程池隔离/信号量隔离 |
| 熔断策略 | 基于慢调用比例或异常比例 | 基于异常比率 |
| 限流 | 基于 QPS,支持流量整形 | 有限的支持 |
| Fallback | 支持 | 支持 |
| 控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
| 配置方式 | 基于代码。也可以基于控制台,但基于控制台重启后失效。 | 基于注解或配置文件,永久生效 |
Sentinel
初识Sentinel
Sentinel是阿里巴巴开源的一款微服务流量控制组件。官网地址: https://sentinelguard.io/zh-cn/index.html

官方提供好了一个核心库——Sentinel客户端,其实就是一个jar包。包含了服务限流、熔断等上面的功能。那微服务引用它,这些功能都不用再去写了。只需要配置规则即可。
配置方式有两种
-
Java编码实现:相对来说比较麻烦,需要学很多api
-
控制台实现:只要服务引入客户端并配置控制台地址,那么微服务中的客户端会和控制台产生交互,控制台可以监控微服务内部的接口运行、限流等情况。在控制台中也可以实现对上面功能规则的配置。不用编码实现了 。
综上,控制台有两个功能
- 接口监控
- 规则配置
综上:Sentinel 的使用可以分为两个部分:
- 核心库(Jar包):不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。在项目中引入依赖即可实现服务限流、隔离、熔断等功能。
- 控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等。
所以,我们用控制台的方式实现。
搭建控制台
-
下载jar包
下载地址,也可以直接使用已有资料提供的版本
sentinel-dashboard-1.8.6.jar -
运行
将jar包放在任意非中文、不包含特殊字符的目录下,重命名为
sentinel-dashboard.jar,方便运行指令的编写。然后在命令行运行如下命令启动控制台:
java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar默认端口是8080,端口变了地址也配一下,项目名称
其它启动时可配置参数可参考官方文档
-
访问
访问http://localhost:8090页面,就可以看到sentinel的控制台了:

需要输入账号和密码,默认都是:sentinel
登录后,即可看到控制台,默认会监控sentinel-dashboard服务本身:

微服务整合
我们在cart-service模块中整合sentinel,连接sentinel-dashboard控制台,步骤如下:
-
引入sentinel依赖
<!--sentinel--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> -
配置控制台
修改
application.yaml文件,添加下面内容:【这样就和控制台关联了】spring: cloud: sentinel: transport: dashboard: localhost:8090 #与控制台关联 -
访问
cart-service的任意端点重启
cart-service,然后访问查询购物车接口【得有了访问才能显示】,sentinel的客户端就会将服务访问的信息提交到sentinel-dashboard控制台。并展示出统计信息:
我们可以在控制台可以看出常用功能
-
实时监控
接口限流等等情况
-
**规则
更好配置对应的规则,推送到服务中
-
簇点链路,
就是单机调用链路。是一次请求进入服务后经过的每一个被Sentinel监控的资源链。默认Sentinel(只)会监控SpringMVC的每一个Endpoint(http接口)【对应controller接口】。限流、熔断等都是针对簇点链路中的资源设置的。而资源名默认就是接口的请求路径
但这里有个问题,它是以请求路径作为簇点资源名称的。但Restful风格的API请求路径一般都相同,这会导致簇点资源名称重复。因此我们要修改配置,把请求方式+请求路径作为簇点资源名称,在Sentinel中就不会重复了
spring: cloud: sentinel: transport: dashboard: localhost:8090 #与控制台关联 http-method-specify: true #开启请求方式前缀然后,重启服务,通过页面访问购物车的相关接口,可以看到sentinel控制台的簇点链路发生了变化:

请求限流
在控制台中实现简单配置就行。访问过就会出现在簇点链路。

点击流控,选QPS【每秒钟请求的数量】、单机阈值,限制每秒钟的并发数量是多少。然后可以用测试工具进行压力测试,验证效果。【ApiFox和Jmeter】

线程隔离
当商品服务出现阻塞或故障时,调用商品服务的购物车服务可能因此而被拖慢,甚至资源耗尽。所以必须限制购物车服务中查询商品这个业务的可用线程数,实现线程隔离。

设置方式和上面请求限流基本一致,选择”并发线程数“即可。后面配置的就是这个资源可以用几个线程。

要测试是否生效的话。我们可以针对性的设置一下。
-
可以主动延长处理时长
public List<ItemDTO> queryItemByIds(@RequestParam("ids") List<Long> ids){ ThreadUtil.sleep(2000); return itemService.queryItemByIds(ids); } -
降低tomcat调低上限

server: port: 8082 tomcat: threads: max: 50 # 允许的最大线程数 accept-count: 50 # 最大排队等待线程数量 max-connections: 100 # 允许的最大连接
然后可以对比设置线程隔离前后压力测试访问接口的影响效果。【比如对查询购物车接口进行压力测试。然后测试本接口和其他接口的性能。观察开启前后的不同】
另外,我们可以更进一步优化线程隔离。把OpenFeign整合Sentinel
修改cart-service模块的application.yml文件,开启Feign的sentinel功能【让Feign也成为ClientSentinel的簇点资源】:
feign:
sentinel:
enabled: true # 开启feign对sentinel的支持
Fallback
前面的线程隔离确实对微服务起到了保护作用。但是被隔离的业务资源耗尽后直接不可用了,所以还可以优化。

从上面的图中可以看出,我们假设出问题的地方是商品服务,因为购物车查询时用到了查询商品的服务,所以才会出错。那我们可以细化流控。对查询商品的服务进行限制。同时,为了避免查商品出错后购物车查询直接出错。我们可以添加Fallback逻辑,请求失败后执行fallback逻辑。具体步骤如下
-
将FeignClient作为Sentinel的簇点资源
在cart-service配置文件配置
feign: sentinel: enabled: true配置完成后会在控制台中以查询购物车接口的子级出现
-
FeignClient的Fallback有两种配置方式
- FallbackClass,无法对远程调用的异常做处理
- FallbackFactory,可以对远程调用的异常做处理,通常都会选择这种
案例:
我们对上面提到的商品查询接口编写fallback
-
自定义类,实现FallbackFactory,编写对某个FeignClient的fallback逻辑
在api包下编写
package com.hmall.api.fallback; import com.hmall.api.client.ItemClient; import com.hmall.api.dto.ItemDTO; import com.hmall.api.dto.OrderDetailDTO; import com.hmall.common.utils.CollUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.openfeign.FallbackFactory; import java.util.Collection; import java.util.List; @Slf4j public class ItemClientFallbackFactory implements FallbackFactory<ItemClient> { @Override public ItemClient create(Throwable cause) { //接收异常 return new ItemClient() { @Override public List<ItemDTO> queryItemByIds(Collection<Long> ids) { log.error("查询商品失败!", cause); //打印异常信息 return CollUtils.emptyList(); //返回一个空集合而不报错 } @Override public void deductStock(List<OrderDetailDTO> items) { log.error("扣减商品库存失败!", cause); throw new RuntimeException(cause); //对应不明白如何处理的异常,抛出去就行,让调用者处理 } }; } } -
将刚刚定义的ItemClientFallbackFactory注册为一个Bean
这时,我们已经有一个config类了【DefaultFeignConfig】。我们可以在原本类中添加。也可以新建一个config类。我们这还是以前者为例
@Bean public ItemClientFallbackFactory itemClientFallbackFactory() { return new ItemClientFallbackFactory(); } -
在ItemClient接口中使用ItemClientFallbackFactory
@FeignClient(value = "cart-service", fallbackFactory = ItemClientFallbackFactory.class)
然后就可以测试了
测试发现,及时查询商品出错,但查购物车是没问题的,只不过价格字段是空的。其他操作不受影响。
服务熔断
通过上面的处理后,我们取得了一定的效果。但是还可以优化。
商品服务出错时,我们查询购物车每次还是会对商品服务进行远程调用,而且远程调用很耗时或者直接等待。如果发现服务挂了或者性能下降不可接受,我们直接熔断就好了,直接拒绝远程调用,直接走Fallback。
当然,熔断不能一直熔断。恢复正常了要恢复请求的。
以上可以通过断路器实现。
熔断是解决雪崩问题的重要手段。思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。

open状态不是持续的。到一定期限会放行一次请求,观察是否正常。
对于规则配置,我们可以通过控制很方便的配置

熔断策略【可以同时设置多个策略】
- 慢调用比例
- 最大RT:超过多久算慢调用
- 比例阈值:慢调用占比多少时熔断
- 熔断时长:open状态持续多久
- 最小请求数:能触发熔断的最小请求总数
- 统计时长:统计的时间范围周期
- 异常比例
- 异常数
当然处理上面的功能,Sentinel还有其他功能。可以参考其官方网站进行学习。
分布式事务
分布式事务问题
一直是微服务中的难点。
我们可以通过项目中的一个业务案例进行认识。
首先我们看看项目中的下单业务整体流程

下单业务,前端请求首先进入订单服务,创建订单并写入数据库。然后订单服务调用购物车服务和库存服务:
- 购物车服务负责清理购物车信息
- 库存服务负责扣减商品库存
在分布式系统中,如果一个业务需要多个服务合作完成,而且每一个服务都有事务,多个事务必须同时成功或失败,这样的事务就是分布式事务。其中的每个服务的事务就是一个分支事务。整个业务称为全局事务。
在案例中,我们知道每一个分支事务就是传统的单体事务,都可以满足ACID特性,但全局事务跨越多个服务、多个数据库,是否还能满足呢?
我们已有的代码中,购物车服务感知不到库存服务的错误。所以依然清理了购物车。这就使得全局事务不满足ACID特性。
这就是分布式事务问题,出现以下情况之一就可能产生分布式事务问题:
- 业务跨多个服务实现
- 业务跨多个数据源实现
接下来研究下如何解决分布式事务问题。
Seata
初识Seata
解决分布式事务的方案有很多,但实现起来都比较复杂,因此我们一般会使用开源的框架来解决分布式事务问题。在众多的开源分布式事务框架中,功能最完善、使用最多的就是阿里巴巴在2019年开源的Seata了。
Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。
官网地址:http://seata.io/,其中的文档、播客中提供了大量的使用说明、源码分析。
其实分布式事务产生的一个重要原因,就是参与事务的多个分支事务互相无感知,不知道彼此的执行状态。因此解决分布式事务的思想非常简单:
就是找一个统一的事务协调者,与多个分支事务通信,检测每个分支事务的执行状态,保证全局事务下的每一个分支事务同时成功或失败即可。大多数的分布式事务框架都是基于这个理论来实现的。

其实就是当局者迷,旁观者清。加一个事务协调者即可。Seata就是这个原理
但实际上更加复杂一点。

Seata事务管理中有三个重要的角色:
- TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。
- TM (Transaction Manager) - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。【全局事务的开始,可以理解为ceateOrder方法的范围。我们告诉它就行。】
- RM (Resource Manager) - 资源管理器:管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态
TC通过TM知道什么时候开始和技术,通过RM知道微服务是否正常。
其中,TM和RM可以理解为Seata的客户端部分,引入到参与事务的微服务依赖中即可。将来TM和RM就会协助微服务,实现本地分支事务与TC之间交互,实现事务的提交或回滚。
而TC服务则是事务协调中心,是一个独立的微服务,需要单独部署。
部署TC服务
-
准备数据库表
Seata支持多种存储模式,但考虑到持久化的需要,我们一般选择基于数据库存储。执行课前资料提供的
《seata-tc.sql》,导入数据库表:
-
准备配置文件
已有资料一个seata目录,其中包含了seata运行时所需要的配置文件:

文件示例如下:
server: port: 7099 #web控制台端口 spring: application: name: seata-server #其实也是一个微服务,这里指定微服务名字。 logging: config: classpath:logback-spring.xml file: path: ${user.home}/logs/seata # extend: # logstash-appender: # destination: 127.0.0.1:4560 # kafka-appender: # bootstrap-servers: 127.0.0.1:9092 # topic: logback_to_logstash console: #控制台账号密码 user: username: admin password: admin seata: #seata配置 config: # support: nacos, consul, apollo, zk, etcd3 type: file #支持多种配置方式,这里指定为file直接在当前文件配置,当然也可以指定为nacos # nacos: # server-addr: nacos:8848 # group : "DEFAULT_GROUP" # namespace: "" # dataId: "seataServer.properties" # username: "nacos" # password: "nacos" registry: # support: nacos, eureka, redis, zk, consul, etcd3, sofa type: nacos nacos: #配置到注册中心,让微服务知道其地址信息 application: seata-server server-addr: nacos:8848 #容器名,可以部署到docker中 group : "DEFAULT_GROUP" namespace: "" # 命名空间【具体可以按项目划分】,不填就是public,分组 username: "nacos" password: "nacos" # server: # service-port: 8091 #If not configured, the default is '${server.port} + 1000' security: secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 tokenValidityInMilliseconds: 1800000 ignore: urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login server: # service-port: 8091 #If not configured, the default is '${server.port} + 1000' max-commit-retry-timeout: -1 max-rollback-retry-timeout: -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 async-committing-retry-period: 1000 rollbacking-retry-period: 1000 timeout-retry-period: 1000 undo: log-save-days: 7 log-delete-period: 86400000 session: branch-async-queue-size: 5000 #branch async remove queue size enable-branch-async-remove: false #enable to asynchronous remove branchSession store: # 数据存储,也有很多方式 # support: file 、 db 、 redis mode: db session: mode: db lock: mode: db db: datasource: druid db-type: mysql driver-class-name: com.mysql.cj.jdbc.Driver # 因为后面部署到容器中,这里mysql写的是容器名 url: jdbc:mysql://mysql:3306/seata?rewriteBatchedStatements=true&serverTimezone=UTC user: root password: 123 # 和一些参数和表 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 max-wait: 5000 # redis: # mode: single # database: 0 # min-conn: 10 # max-conn: 100 # password: # max-total: 100 # query-limit: 1000 # single: # host: 192.168.150.101 # port: 6379 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 -
Docker部署
我们将整个seata文件夹拷贝到虚拟机的
/root目录:
需要注意,要确保nacos、mysql都在hm-net网络中。如果某个容器不再hm-net网络
在虚拟机的
/root目录执行下面的命令:【两个端口,应该是与微服务,一个是控制台】docker run --name seata \ -p 8099:8099 \ -p 7099:7099 \ -e SEATA_IP=192.168.150.101 \ -v ./seata:/seata-server/resources \ --privileged=true \ --network hm-net \ -d \ seataio/seata-server:1.5.2当然我们应该利用已有的镜像
seata-1.5.2.tar。免得下载镜像了。注意:
我们一定要把配置文件自己的信息填充上。否则出错。
部署成功!!!


微服务集成Seata
参与分布式事务的每一个微服务都需要集成Seata,我们以trade-service为例。
注意:如果在api模块或common模块引入,那么除了这三个使用seata的微服务需要配置,其它没有使用seata的模块也要配置seata,因为引入的是starter场景启动器,否则报错。
集成步骤如下:
-
引入依赖
<!--统一配置管理--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!--读取bootstrap文件--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency> <!--seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency> -
配置TC地址,在
application.yml中添加配置,让微服务找到TC服务地址:
TC很重要,我们实际工作中TC很可能是个集群,所以比较复杂,而且写死也不太好。既然我们上面已经把它注册到nacos中了。我们可以用nacos进行配置所以我们直接把下面配置复制到nacos中去。
shared-seata.yaml
seata: registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址 type: nacos # 注册中心类型 nacos nacos: server-addr: 192.168.150.101:8848 # nacos地址 namespace: "" # namespace,默认为空 group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP application: seata-server # seata服务名称 username: nacos password: nacos tx-service-group: hmall # 事务组名称 service: vgroup-mapping: # 事务组与tc集群的映射关系 hmall: "default"spring cloud中已经配置了nacos地址,这里之所以还需要nacos地址,是因为Seata都不确定用到是哪个类型的服务中心,更不会去找nacos地址,所以这里直接再配一下就好了
命名空间可以理解成数据隔离的东西,命名空间->分组->微服务->集群【具体运行微服务中的某个集群,可以理解为机房,每个机房就是一个集群】。有了这些信息,才能确定是哪个具体的服务
事务组可以理解为集群。一个机房一个组
然后,改造
trade-service模块,添加bootstrap.yaml:【哪些微服务需要做这样的集成呢——事务参与者,比如这俩trade、item、cart服务】server: port: 8085 feign: okhttp: enabled: true # 开启OKHttp连接池支持 sentinel: enabled: true # 开启Feign对Sentinel的整合 hm: swagger: title: 交易服务接口文档 package: com.hmall.trade.controller db: database: hm-trade参考上述办法分别改造
hm-cart和hm-item两个微服务模块。
上面配置基本上是死的,CV就行
接下来,我们就可以利用Seata解决分布式事务问题了
解决方案
Seata为解决分布式事务针对不同场景提供了多种模式,最有代表性的模式有两种模式
- XA
- TCC
- AT
- SAGA
这里我们以XA模式和AT模式来介绍
XA模式
XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,这套标准有数十年的历史了,几乎所有主流的关系型数据库都对 XA 规范 提供了支持。Seata的XA模式如下:

事务执行完,不会提交,锁不会释放的。等带所有分支都执行完才提交,避免不一致。调用完,TM知道是不是执行完了
一阶段的工作:
- RM注册分支事务到TC
- RM执行分支业务sql但不提交
- RM报告执行状态到TC
二阶段的工作:
- TC检测各分支事务执行状态
- 如果都成功,通知所有RM提交事务
- 如果有失败,通知所有RM回滚事务
- RM接收TC指令,提交或回滚事务
优点:
- 事务的强一致性,满足ACID原则。
- 常用数据库都支持,实现简单,并且没有代码侵入
缺点:【干等,别人也干不了】
- 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
- 依赖关系型数据库实现事务
Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:
-
修改application.yml文件(每个参与事务的微服务),开启XA模式【因为抽取到了nacos中,可以在nacos中共享shared-seata.yaml配置文件中设置】:
seata: data-source-proxy-mode: XA -
给发起全局事务的入口方法添加
@GlobalTransactional注解,本例中是OrderServiceImpl中的create方法:【另外在分支事务方法中添加Transactional注解,方便事务回滚】【Transactional注解在不侵入业务代码的情况下,为方法或类添加事务控制能力,确保数据库操作ACID】@Override @GlobalTransactional public Long createOrder(OrderFormDTO orderFormDTO) {}
然后就可以测试。把购物车中某商品数改成零,提交订单,查看相应情况。
AT模式
Seata主推的是AT模式【默认AT】,AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷。

阶段一RM的工作:
- 注册分支事务
- 记录undo-log(数据快照)
- 执行业务sql并提交
- 报告事务状态
阶段二提交时RM的工作:
- 删除undo-log即可
阶段二回滚时RM的工作:
- 根据undo-log恢复数据到更新前
在这个过程中可以看出,会出现脏读的现象,出现数据短暂不一致的现象。
简述AT模式与XA模式最大的区别是什么?
- XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
- XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
- XA模式强一致;AT模式最终一致【出现数据短暂不一致的现象】
这两个模式具体用哪个,看实际业务的需求了,看对性能和一致性平衡。
实现AT模式步骤如下:
-
首先,添加资料中的seata-at.sql到每个微服务对应的数据库中:【可以看出,这就是上面提到存快照的】
-- for AT mode you must to init this sql for you business database. the seata server not need it. CREATE TABLE IF NOT EXISTS `undo_log` ( `branch_id` BIGINT NOT NULL COMMENT 'branch transaction id', `xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id', `context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization', `rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info', `log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status', `log_created` DATETIME(6) NOT NULL COMMENT 'create datetime', `log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime', UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table'; -
修改application.yml文件,将事务模式修改为AT模式:【和上面相同,可以在nacos中设置。其实默认就是AT】
seata: data-source-proxy-mode: AT

浙公网安备 33010602011771号