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

SpringCloud微服务:服务保护和分布式事务

本文研究微服务架构的服务保护与分布式事务。服务保护方面,提出限流、隔离、熔断三种策略,搭配使用 Sentinel 服务降级技术。分布式事务方面,列举使用 Seata 的 XA 和 AT 模式,为微服务稳定性提供解决方案。

在微服务架构中,远程调用面临业务健壮性和级联失败问题。

例如,购物车服务依赖商品服务查询信息,一旦商品服务故障或响应延迟,可能导致购物车服务调用失败、响应变慢甚至阻塞,进而影响用户体验和整个服务的可用性,甚至引发级联故障,拖累整个微服务集群性能。

缓存雪崩产生原因:

级联失败导致的资源耗尽

  • 问题描述:当某个服务(如商品服务)的并发请求过高时,可能会导致其资源被耗尽。这使得商品服务的响应时间显著增加,甚至出现超时或阻塞的情况。

  • 影响

    • 服务响应延迟:依赖商品服务的购物车服务需要等待商品服务的响应,这会导致购物车服务的响应时间也大幅增加。

    • 资源耗尽:如果购物车服务的请求量较大,它会占用更多的连接资源来等待商品服务的响应,从而导致购物车服务自身的资源(如Tomcat连接池)也被耗尽。

    • 级联阻塞:购物车服务的阻塞可能会进一步影响其他依赖购物车服务的服务,最终导致整个微服务集群的性能急剧下降,甚至不可用。


1.微服务保护

保证服务运行的健壮性,避免级联失败导致的雪崩问题,就属于微服务保护。这章我们就一起来学习一下微服务保护的常见方案以及对应的技术。

1.1.服务保护方案

微服务保护的方案有很多,比如:

  • 请求限流

  • 线程隔离

  • 服务熔断

这些方案或多或少都会导致服务的体验上略有下降,比如请求限流,降低了并发上限;线程隔离,降低了可用资源数量;服务熔断,降低了服务的完整度,部分服务变的不可用或弱可用。因此这些方案都属于服务降级的方案。但通过这些方案,服务的健壮性得到了提升。


1.1.1.请求限流

控制流量


1.1.2.线程隔离


1.1.3.服务熔断

线程隔离虽然避免了雪崩问题,但故障服务(商品服务)依然会拖慢购物车服务(服务调用方)的接口响应速度。而且商品查询的故障依然会导致查询购物车功能出现故障,购物车业务也变的不可用了。

所以,我们要做两件事情:

  • 编写服务降级逻辑:就是服务调用失败后的处理逻辑,根据业务场景,可以抛出异常,也可以返回友好提示或默认数据。

  • 异常统计和熔断:统计服务提供方的异常比例,当比例过高表明该接口会影响到其它服务,应该拒绝调用该接口,而是直接走降级逻辑。


1.2.Sentinel

微服务保护的技术有很多,但在目前国内使用较多的还是Sentinel,所以接下来我们学习Sentinel的使用。


1.2.1.安装Sentinel

1.解压jar包

2.登录localhost:8090

3.访问cart-service的任意端点

重启cart-service,然后访问查询购物车接口,sentinel的客户端就会将服务访问的信息提交到sentinel-dashboard控制台。并展示出统计信息:

点击簇点链路菜单,会看到下面的页面:

在微服务架构中,簇点链路是指一次请求进入服务后,经过的所有被Sentinel监控的资源节点所形成的调用路径。默认情况下,Sentinel会自动监控Spring MVC中的每一个Endpoint(接口)。

例如,/carts接口路径就是一个典型的簇点。通过Sentinel,我们可以针对该接口实施限流、熔断、隔离等保护措施,从而有效保障服务的稳定性和可靠性。

默认情况下Sentinel会把路径作为簇点资源的名称,无法区分路径相同但请求方式不同的接口,查询、删除、修改等都被识别为一个簇点资源,这显然是不合适的。

所以我们可以选择打开Sentinel的请求方式前缀,把请求方式 + 请求路径作为簇点资源名

1.在cart-serviceapplication.yml中添加下面的配置:

spring:cloud:sentinel:transport:dashboard: localhost:8090http-method-specify: true # 开启请求方式前缀

2.重启服务,通过页面访问购物车的相关接口,可以看到sentinel控制台的簇点链路发生了变化:


1.3.请求限流

1.在簇点链路后面点击流控按钮,即可对其做限流配置

2.我们利用Jemeter做限流测试,我们每秒发出10个请求(QPS),最终监控结果如下

3.查看Sentinel控制台

可以看出这个接口的通过QPS稳定在6附近,而拒绝的QPS在4附近,符合我们的预期。


1.4.线程隔离

限流可以降低服务器压力,尽量减少因并发流量引起的服务故障的概率,但并不能完全避免服务故障。一旦某个服务出现故障,我们必须隔离对这个服务的调用,避免发生雪崩。

比如,查询购物车的时候需要查询商品,为了避免因商品服务出现故障导致购物车服务级联失败,我们可以把购物车业务中查询商品的部分隔离起来,限制可用的线程资源:

这样,即便商品服务出现故障,最多导致查询购物车业务故障,并且可用的线程资源也被限定在一定范围,不会导致整个购物车服务崩溃。

所以,我们要对查询商品的FeignClient接口做线程隔离。


1.4.1.OpenFeign整合Sentinel

1.修改cart-service模块的application.yml文件,开启Feign的sentinel功能

feign:sentinel:enabled: true # 开启feign对sentinel的支持

默认SpringBoot的tomcat最大线程数是200,允许的最大连接是8492,单机测试很难打满。

2.配置cart-service模块的application.yml文件,修改tomcat连接

server:port: 8082tomcat:threads:max: 50 # 允许的最大线程数accept-count: 50 # 最大排队等待数量max-connections: 100 # 允许的最大连接

3.重启cart-service服务,观察到查询商品的FeignClient自动变成了一个簇点资源


1.4.2.配置线程隔离

1.点击查询商品的FeignClient对应的簇点资源后面的流控按钮,在弹出的表单中填写下面内容

2.利用Jemeter测试,发送请求

3.查看Sentinel控制台


1.5.服务熔断

在上节课,我们利用线程隔离对查询购物车业务进行隔离,保护了购物车服务的其它接口。由于查询商品的功能耗时较高(我们模拟了500毫秒延时),再加上线程隔离限定了线程数为5,导致接口吞吐能力有限,最终QPS只有10左右。这就导致了几个问题:

第一,超出的QPS上限的请求就只能抛出异常,从而导致购物车的查询失败。但从业务角度来说,即便没有查询到最新的商品信息,购物车也应该展示给用户,用户体验更好。也就是给查询失败设置一个降级处理逻辑。

第二,由于查询商品的延迟较高(模拟的500ms),从而导致查询购物车的响应时间也变的很长。这样不仅拖慢了购物车服务,消耗了购物车服务的更多资源,而且用户体验也很差。对于商品服务这种不太健康的接口,我们应该直接停止调用,直接走降级逻辑,避免影响到当前服务。也就是将商品查询接口熔断


1.5.1.编写降级逻辑

1.在hm-api模块中给ItemClient定义降级处理类,实现FallbackFactory

package com.hmall.api.client.fallback;@Slf4j
public class ItemClientFallback implements FallbackFactory<ItemClient> {@Overridepublic ItemClient create(Throwable cause) {return new ItemClient() {@Overridepublic List<ItemDTO> queryItemByIds(Collection<Long> ids) {log.error("远程调用ItemClient#queryItemByIds方法出现异常,参数:{}", ids, cause);// 查询购物车允许失败,查询失败,返回空集合return CollUtils.emptyList();}@Overridepublic void deductStock(List<OrderDetailDTO> items) {// 库存扣减业务需要触发事务回滚,查询失败,抛出异常throw new BizIllegalException(cause);}};}
}

2.在hm-api模块中的com.hmall.api.config.DefaultFeignConfig类中将ItemClientFallback注册为一个Bean

@Beanpublic ItemClientFallbackFactory itemClientFallbackFactory(){return new ItemClientFallbackFactory();}

3.在hm-api模块中的ItemClient接口中使用ItemClientFallbackFactory


1.5.2.服务熔断

        在微服务架构中,当查询商品的响应时间(RT)较高(例如模拟的 500ms),会导致依赖该接口的查询购物车服务的响应时间也显著增加。这不仅会拖慢购物车服务的性能,消耗更多资源,还会严重影响用户体验。

        对于这种性能不佳的商品服务接口,我们可以通过熔断机制来避免其对当前服务的影响。具体来说,当商品服务接口的响应时间过长或异常比例过高时,应立即停止对该接口的调用,转而执行降级逻辑。这样可以防止故障扩散,保护购物车服务的正常运行。一旦商品服务接口恢复正常,再重新允许对该接口的调用。这种模式正是断路器的工作原理。

        在 Sentinel 中,断路器机制不仅能够统计接口的慢请求比例,还能统计异常请求比例。当这些比例超过预设阈值时,Sentinel 会自动熔断该接口,拦截所有对该接口的访问请求,并执行降级逻辑。一旦接口的健康状况恢复正常,Sentinel 会自动恢复对该接口的调用。这种方式能够有效保护系统资源,提升用户体验,同时确保服务的高可用性。

断路器的工作状态切换有一个状态机来控制:

状态机包括三个状态:

  • closed:关闭状态,断路器放行所有请求,并开始统计异常比例、慢请求比例。超过阈值则切换到open状态

  • open:打开状态,服务调用被熔断,访问被熔断服务的请求会被拒绝,快速失败,直接走降级逻辑。Open状态持续一段时间后会进入half-open状态

  • half-open:半开状态,放行一次请求,根据执行结果来判断接下来的操作。

    • 请求成功:则切换到closed状态

    • 请求失败:则切换到open状态


我们可以在控制台通过点击簇点后的熔断按钮来配置熔断策略:

这种是按照慢调用比例来做熔断,上述配置的含义是:

  • RT超过200毫秒的请求调用就是慢调用

  • 统计最近1000ms内的最少5次请求,如果慢调用比例不低于0.5,则触发熔断

  • 熔断持续时长20s


2.分布式事务

首先我们看看项目中的下单业务整体流程:

在微服务架构中,订单、购物车和商品服务分别部署在不同的微服务中,每个微服务都有自己的独立数据库。因此,在下单流程中,业务操作会跨越多个数据库,涉及多个微服务的本地事务:

  • 订单服务:负责处理下单事务。

  • 购物车服务:负责清理购物车事务。

  • 库存服务:负责扣减库存事务。

这些本地事务相互关联,共同构成了一个全局事务。每个微服务的本地事务可以视为一个分支事务,而多个分支事务的组合则构成了全局事务。为了确保业务的完整性和一致性,我们必须保证整个全局事务要么全部成功,要么全部失败。

虽然每个分支事务本身是传统的单体事务,能够满足 ACID(原子性、一致性、隔离性、持久性)特性,但全局事务跨越多个服务和数据库,无法直接依赖单体事务的 ACID 特性来保证一致性。因此,我们需要引入分布式事务解决方案来确保全局事务的一致性。

分布式事务解决方案通过协调各个分支事务的行为,确保全局事务的原子性和一致性。常见的解决方案包括两阶段提交、补偿事务(TCC)、本地消息表、事件溯源等。这些方案各有优缺点,需要根据具体的业务场景和技术栈进行选择。


2.1.认识Seata


2.2.部署TC服务

2.2.1.准备数据库表

1.导入数据库表

2.我们将整个seata文件夹拷贝到虚拟机的/root目录


2.2.2.Docker部署

1.将某容器加入指定网络

docker network connect [网络名] [容器名]

2.在虚拟机的/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

2.3.微服务集成Seata

参与分布式事务的每一个微服务都需要集成Seata,我们以trade-service为例。

2.3.1.引入依赖

为了方便各个微服务集成seata,我们需要把seata配置共享到nacos,因此trade-service模块不仅仅要引入seata依赖,还要引入nacos依赖:

<!--统一配置管理--><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>

2.3.2.改造配置

1.nacos上添加一个共享的seata配置,命名为shared-seata.yaml

seata:registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址type: nacos # 注册中心类型 nacosnacos:server-addr: 192.168.150.101:8848 # nacos地址namespace: "" # namespace,默认为空group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUPapplication: seata-server # seata服务名称username: nacospassword: nacostx-service-group: hmall # 事务组名称service:vgroup-mapping: # 事务组与tc集群的映射关系hmall: "default"

2.改造trade-service模块,添加bootstrap.yaml

spring:application:name: trade-service # 服务名称profiles:active: devcloud:nacos:server-addr: 192.168.150.101 # nacos地址config:file-extension: yaml # 文件后缀名shared-configs: # 共享配置- dataId: shared-jdbc.yaml # 共享mybatis配置- dataId: shared-log.yaml # 共享日志配置- dataId: shared-swagger.yaml # 共享日志配置- dataId: shared-seata.yaml # 共享seata配置

3.改造application.yaml文件

server:port: 8085
feign:okhttp:enabled: true # 开启OKHttp连接池支持sentinel:enabled: true # 开启Feign对Sentinel的整合
hm:swagger:title: 交易服务接口文档package: com.hmall.trade.controllerdb:database: hm-trade

2.3.3.测试

将其上的@Transactional注解改为Seata提供的@GlobalTransactional


2.4.XA模式

Seata支持四种不同的分布式事务解决方案:

  • XA

  • TCC

  • AT

  • SAGA

这里我们以XA模式AT模式来给大家讲解其实现原理。

XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。

2.4.1.两阶段提交


2.4.2.优缺点

优点缺点
事务的强一致性,满足ACID原则因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
常用数据库都支持,实现简单,并且没有代码侵入依赖关系型数据库实现事务

2.4.3.实现步骤

1.指定要采用的分布式事务模式。我们可以在Nacos中的共享shared-seata.yaml配置文件中设置:

seata:data-source-proxy-mode: XA

2.利用@GlobalTransactional标记分布式事务的入口方法:


2.5.AT模式

AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷。

2.5.1.Seata的AT模型

基本流程图:


2.5.2.AT与XA的区别

区别点XA 模式AT 模式
事务提交阶段一阶段不提交事务,仅锁定资源,等待二阶段决定是否提交。一阶段直接提交本地事务,不锁定资源。
回滚机制依赖数据库的 XA 机制,在二阶段回滚时释放资源并撤销一阶段的操作。利用数据快照(undo-log)实现数据回滚,不依赖数据库的 XA 机制。
一致性强一致性,事务要么全部提交,要么全部回滚,中间状态不允许存在。最终一致性,通过补偿机制确保最终状态一致,但中间可能存在短暂不一致。
性能性能较低,因为一阶段锁定资源,可能导致资源争用和阻塞。性能较高,因为一阶段直接提交,减少了资源锁定时间。
业务侵入性对业务代码侵入性较低,主要依赖数据库的 XA 支持。对业务代码无侵入,业务逻辑无需修改,只需配置 Seata 即可。
适用场景适用于对一致性要求极高的场景,如金融核心系统。适用于大多数业务场景,尤其是对性能要求较高且可以接受最终一致性的场景。
资源锁定一阶段锁定资源,可能导致资源长时间占用,影响并发性能。不锁定资源,减少资源争用,提升并发性能。
数据库支持需要数据库支持 XA 协议,部分数据库可能不支持或支持效果不佳。不依赖数据库的 XA 协议,适用于更多类型的数据库。
复杂度实现复杂,依赖数据库的 XA 机制,配置和维护成本较高。实现相对简单,通过 Seata 的中间件实现,配置和维护成本较低。
适用范围由于性能和资源锁定问题,适用范围相对较窄。适用范围广,适合大多数分布式事务场景,尤其在微服务架构中表现良好。

总结

  • XA 模式:强一致性,依赖数据库 XA 机制,一阶段锁定资源,性能较低,适用范围较窄,但对一致性要求极高的场景非常适用。

  • AT 模式:最终一致性,利用数据快照实现回滚,一阶段直接提交,性能较高,对业务无侵入,适用范围广,适合大多数分布式事务场景。

在实际应用中,企业可以根据业务需求和对一致性的要求选择合适的事务模式。对于大多数业务场景,AT 模式因其简单易用和高性能特点,成为分布式事务的首选方案。


大功告成!

相关文章:

  • 三次握手建立连接,四次挥手释放连接——TCP协议的核心机制
  • Linux 下的 PM2 完整指南
  • linux基于内存实现jar文件搜索
  • 如何有效监控JVM环境,保障应用性能
  • Elasticsearch:使用 ES|QL 进行地理空间距离搜索
  • 动态规划3——背包类动态规划详解
  • elasticSearch是什么,如何使用,有什么用
  • 考研系列—408真题操作系统篇(2015-2019)
  • Windows环境变量原理(用户变量与系统变量)(用户环境变量、系统环境变量)
  • centos6.5 老旧系统编译glib-2.58.3.tar.bz2
  • vue-property-decorator实践(一)
  • 如何通过插件系统打造个性化效率工作流
  • AUTOSAR图解==>AUTOSAR_TR_InteroperabilityOfAutosarTools
  • PEP 8: E302 expected 2 blank lines, found 0
  • [vela os_5] 中断系统 | 任务调度 | 日志系统
  • node.js连接mysql写接口(一)
  • jupyter notebook的相关知识及可能遇到的问题
  • spring-ai MCP Server运行一段时间后断联2
  • Spring Boot3批式访问Dify聊天助手接口
  • 力扣-70.爬楼梯
  • 零基础学习网站开发/沈阳高端关键词优化
  • 做网站如何被收录/国家职业技能培训学校
  • 华为云免费服务器/广州seo关键词优化是什么
  • html购物网站怎么做/网络seo软件
  • 最新网站推广哪家好/四川百度推广排名查询
  • 天津时时彩网站制作/seo运营