微服务保护——Sentinel

什么是微服务保护?

微服务保护是一系列用于保障微服务架构稳定、可靠运行的策略与技术手段,在复杂的分布式微服务系统里,它能避免局部故障引发连锁反应,从而维持整个系统的可用性,主要涵盖以下几个关键部分:

  • 请求限流
    • 原理:限制进入微服务系统或某个具体微服务实例的流量速率。例如,设定每秒最多处理 1000 个请求,当请求量即将超过该阈值时,会直接拒绝多余请求。
    • 作用:防止突发大流量冲垮服务,确保系统资源能合理分配,从容应对正常流量负载,像电商促销场景,大量用户集中访问下单服务,限流可避免服务瘫痪。
  • 服务熔断
    • 原理:实时监测微服务间的调用链路,当某个被调用的下游服务频繁出现错误(如超时、异常返回等),达到一定失败比例,调用方会 "熔断" 与它的连接,暂时不再发起调用,直接返回预设的兜底数据 。
    • 作用:避免故障服务持续拖累调用方,把故障隔离在局部,举例来说,若商品推荐服务依赖的库存查询服务崩溃,熔断机制下,推荐服务能快速切断调用链路,继续为用户展示部分可用内容。
  • 服务降级
    • 原理:在系统面临高压力,资源紧张时,有计划地舍弃部分非核心功能或降低服务质量。比如,关闭某些耗费资源的个性化推荐模块,只提供通用推荐列表。
    • 作用:保障核心业务流程的流畅,牺牲次要功能来维持系统基本运转,确保多数用户的关键诉求能得到满足。
  • 负载均衡
    • 原理:把外部请求均匀分发给多个微服务实例。常见的算法包括轮询、加权轮询、随机等,像轮询就是按顺序依次将请求分配到各个实例。
    • 作用:避免单个实例因接收过多请求而过载,充分利用集群里的所有实例资源,提升整体吞吐能力 。
  • 容错处理
    • 原理:微服务调用出现错误时,通过重试、补偿事务等机制,努力让流程继续推进。例如,一次数据库写入失败,在一定次数内重试写入。
    • 作用:增强系统应对各类异常状况的韧性,减少因临时性错误导致的业务中断,提升用户体验。

微服务保护避免了哪些问题?

  • 雪崩效应
    • 问题描述:在微服务体系里,服务间调用关系错综复杂,一旦某个基础服务出现故障,比如频繁超时或持续返回错误,调用它的上游服务会因等待响应而占用大量资源。随着越来越多上游服务受牵连,资源耗尽会让更多服务不可用,最终整个系统如同雪崩般瘫痪。
    • 避免方式:熔断机制在检测到下游服务错误率过高时,迅速切断调用链路,让上游服务直接返回兜底数据,避免无意义等待,将故障隔离在局部,防止故障传播引发雪崩。例如,支付服务依赖的风控服务出现大量超时,支付服务熔断对风控服务的调用,不影响支付流程继续处理常规订单。
  • 服务过载
    • 问题描述:面对突发的流量高峰,例如电商平台限时抢购、热门事件引发的流量井喷,微服务可能接收远超处理能力的请求数量,导致响应时间急剧拉长,资源(CPU、内存等)被过度消耗,后续正常流量也无法妥善处理。
    • 避免方式:限流策略限定进入微服务的流量速率,只处理预设范围内的请求,拒绝多余的流量,确保服务资源合理分配,维持基本的响应性能。像某社交平台突发热点话题,帖子详情查看服务设置每秒 1 万次请求的限流,避免被海量请求拖垮。
  • 级联故障
    • 问题描述:微服务 A 调用微服务 B,B 又调用微服务 C,若 C 出现问题,B 可能因无法获取正确数据而产生错误输出,进一步导致 A 出现异常,一连串的异常在服务链路里不断传递,扩大故障影响范围。
    • 避免方式:熔断、降级以及容错处理协同发挥作用。熔断及时断开故障链路;降级舍弃部分非核心功能,保证核心链路畅通;容错处理利用重试、补偿事务等手段修复可能的异常环节,中断级联故障的发展。例如,内容展示服务依赖图片处理服务,图片服务出问题时,内容服务降级只展示文字内容,避免自身崩溃。
  • 资源耗尽
    • 问题描述:不合理的服务调用关系或者无限循环的业务逻辑,可能使得微服务不断申请新的资源,例如持续开启新线程处理任务,最终耗尽系统的内存、CPU 等关键资源,让整个系统陷入停滞。
    • 避免方式:负载均衡将请求均匀分散到多个微服务实例,避免单个实例因过度请求而无节制消耗资源;同时,线程池隔离等技术确保每个服务调用占用合理的资源份额,防止资源耗尽。如电商下单服务有多个实例,负载均衡器合理分配请求,各个实例有序处理,不会某一个实例因过多订单请求把资源耗尽。
  • 服务质量下降
    • 问题描述:系统长期处于高负载或局部故障频发状态,即使勉强维持运行,也会造成响应延迟、数据准确性降低等情况,严重影响用户体验。
    • 避免方式:降级策略主动舍弃部分对体验影响较小的功能,集中精力保障核心功能的响应速度与准确性,容错机制也修复小的异常,维持稳定的服务质量。比如,视频平台在网络拥堵时,降低视频分辨率这一非核心功能的优先级,优先保障视频流畅播放。

具体实现------Sentinel

Sentinel安装与使用

Sentinel 是阿里巴巴开源的一款面向分布式服务架构的轻量级流量控制与熔断降级框架,在保障微服务系统稳定、可靠运行方面表现出色。

Sentinel 的使用可以分为两个部分:

  • 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等。

1.搭建控制台

1.1.下载jar包

下载地址:Sentinel控制台下载地址https://github.com/alibaba/Sentinel/releases

1.2.运行

把下载下来的jar包放在一个没有中文的目录下打开CMD用以下命令运行:

java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

其它启动时可配置参数可参考官方文档:

Sentinel启动配置项https://github.com/alibaba/Sentinel/wiki/%E5%90%AF%E5%8A%A8%E9%85%8D%E7%BD%AE%E9%A1%B9

1.3.访问

访问http://localhost:8090页面,就可以看到sentinel的控制台了

账号和密码默认都是sentinel,登录之后就可以看到sentinel的控制台了,默认sentinel健康的就是自己。

2.微服务整合Sentinel

我们在需要进行微服务保护的微服务模块中整合sentinel,连接Sentinel-dashboard控制台

2.1.导入依赖

<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId> 
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

**注意:**这里没有加版本号是因为我们在父模块中添加了对cloud的管理

...省略其他配置

<properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <org.projectlombok.version>1.18.20</org.projectlombok.version>
        <spring-cloud.version>2021.0.3</spring-cloud.version>
        <spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
        <mybatis-plus.version>3.5.5</mybatis-plus.version>
        <hutool.version>5.8.11</hutool.version>
        <mysql.version>8.0.23</mysql.version>
    </properties>

    <!-- 对依赖包进行管理 -->
    <dependencyManagement>
        <dependencies>
            <!--spring cloud-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud alibaba-->
            <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>
            <!-- 数据库驱动包管理 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <!-- mybatis plus 管理 -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <!--hutool工具包-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <!-- lombok 管理 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${org.projectlombok.version}</version>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

...省略其他配置

2.2.配置控制台

在application.yaml中添加以下内容:

spring:
  cloud: 
    sentinel:
      transport:
        dashboard: localhost:8090

2.3.访问测试

重启该微服务模块,并访问该微服务的任意端点,比如:我访问的是购物车模块:

在刷新Sentinel控制台之后,可以看到多出来了一个购物车模块。

点击簇点链路菜单查看:

所谓簇点链路,就是单机调用链路,是一次请求进入服务后经过的每一个被Sentinel监控的资源。默认情况下,Sentinel会监控Spring MVC的每一个接口。

因此,我们看到/cars这个接口路径就是其中一个簇点,我们可以对其进行限流、熔断、隔离等保护措施。

**注意:**我们的SpringMVC接口是按照Restful风格设计,默认情况下Sentinel会把路径作为簇点资源的名称,无法区分路径相同但请求方式不同的接口,查询、删除、修改等都被识别为一个簇点资源,这显然是不合适的。

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

首先,在购物车模块的application.yaml中添加下面的配置:

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

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

1.请求限流

在簇点链路后面点击流控按钮,即可对其做限流配置,比如我们对购物车的查询接口进行操作:

在弹出的菜单中填写如图内容:

这样就把查询购物车列表这个簇点资源的流量限制在了每秒10个,也就是最大QPS为10。

由于我们一秒钟不能刷新10次,所以我们通过Jemeter工具进行测试:

添加配置:

启动测试:

查看结果树:

可以看到我们的请求有些是能够访问的,有些是访问失败的

查看Sentinel控制台:

在这里我们可以很清楚的看到通过和拒绝的请求, 并且还有请求的响应时间,这就更加利于我们对微服务的监控和保护。

2.线程隔离

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

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

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

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

1.OpenFeign整合Sentinel

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

**注意:**我们因为需要测试Tomcat的请求被打满的场景,但是默认情况下SpringBoot项目的tomcat最大线程数是200,允许的最大连接是8492,单机测试很难打满,所以我们需要配置一下cart-service模块的application.yml文件,修改tomcat连接:

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

为查询商品添加500毫秒的延迟,模拟网络延迟

重启服务查看Sentinel控制台可以看到,查询购物车下的查询商品变成了一个簇点资源,我们也可以对查询商品进行对应的配置:

2.配置线程隔离

2.1.限制查询商品的并发线程数

这里设置的并发线程数为5表示的是如果一秒钟能处理2次请求,那5个线程一秒钟的处理请求数就是10左右,超出的请求就会直接拒绝。

3.测试

利用Jemeter测试,每秒发送100个请求:

最终的结果是:

在这50秒内,我们去访问购物车的其他接口,比如添加商品,修改商品数量,可以看到并不会受到查询购物车接口的影响:

在服务隔离中我们对查询购物车业务进行隔离,保护了购物车服务的其它接口,我们通过给查询商品加了500毫秒的延迟,模拟网络延迟,达到了我们想要测试的效果:QPS为10左右,但是这样就会有几个问题:

1.超出的QPS上限的请求就只能抛出异常,从而导致购物车的查询失败,直接显示失败,这样对客户的体验不是很好,所以就算我们没有查询商品成功,也要显示购物车给客户查看,提高客户体验,解决方案: 给查询失败设置一个降级处理逻辑

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

3.服务降级

3.1.编写降级逻辑

在我们的请求触发限流或者是降级逻辑的时候,不一定要直接报错,我们也可以返回一些默认数据或者友好提示,这样用户体验会更好。

降级逻辑主要有两种方式:

1.FallbackClass,无法对远程调用的异常做处理。

2.FallbackFactory,可以对远程调用的异常做处理,我们一般选择这种方式。

这里演示第二种方式,因为,查询购物车接口,涉及到查询商品,商品是一个单独的微服务模块,他们两个是通过OpenFeign进行网络通信的。

3.1.1.为ItemClient接口,编写降级处理类

这里我把网络通信相关的部分提取到了一个微服务模块中,所以在这个模块下添加:

3.1.2.将ItemClientFallbackFactory注册为bean

3.1.3.在ItemClient接口中使用ItemClientFallbackFactory

3.2.重启服务设置Sentinel规则,进行测试

可以看到,当我们设置了服务降级逻辑之后,被限流的请求并没返回失败,而是返回了旧数据,正确的请求,则是返回的是查询到的数据,但是,还是有一个问题,就是我们上面所提到的购物车服务延迟过高。

4.服务熔断

因为购物车服务的查询商品的响应时间较高(这里通过设置500ms的延迟来模拟),所以这样就会导致购物车服务的响应时间也变高了,这样就拖垮了购物车服务,造成浪费购物车服务的更多资源,而且客户的体验也很差,所以我们就要通过服务熔断,对于这种响应时间很慢的服务进行熔断,直接让他走服务降级逻辑避免影响到当前服务,当商品服务接口恢复正常后,再允许调用。

Sentinel中的断路器不仅可以统计某个接口的慢请求比例 ,还可以统计异常请求比例 。当这些比例超出阈值时,就会熔断该接口,即拦截访问该接口的一切请求,降级处理;当该接口恢复正常时,再放行对于该接口的请求。

断路器的工作状态:

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

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

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

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

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

4.1.配置熔断规则

配置解释:

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

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

  • 熔断持续时长5s

4.2.使用Jemeter测试

可以通过上图看到,配置了服务熔断之后的RT时长大大缩减了,因为我们为购物车的查询商品设置了500ms的延迟大于我们配置的熔断规则,所以购物车服务就被断路器统计慢请求比例,直接走服务降级快速返回默认值,大大的提升了服务的性能。

相关推荐
安全方案5 小时前
2024数据湖架构实践案例(附资料)
架构·数据湖
李新_7 小时前
一文聊聊基于OkHttp封装STOMP实践
android·架构
videring10 小时前
ckeditor5-engine(2)——Model
前端·javascript·架构
长安不及十里11 小时前
Spring Security 搭建一套基于 JWT 的 OAuth 2.0 架构
数据库·spring·架构
qxlxi11 小时前
【mybatis】Mybatis整体架构解析
架构·mybatis
XianxinMao11 小时前
云架构:考量与框架
架构
qq_1715388511 小时前
Spring Cloud微服务多模块架构:父子工程搭建实践
spring cloud·微服务·架构
我是鸹貔13 小时前
微服务-Eureka
微服务·eureka·架构
YaHuiLiang16 小时前
小微互联网公司与互联网创业公司的技术之殇 - "新"技术的双刃剑
前端·后端·架构