Sentinel — 微服务保护

微服务架构将大型应用程序拆分为多个小而独立的服务,每个服务可以独立部署和扩展。然而,微服务系统需要面对的挑战也随之增加,例如服务之间的依赖、分布式环境下的故障传播和安全问题。因此,微服务保护措施是确保系统在高并发、资源消耗和恶意攻击等压力下依然稳定运行的重要手段。

保证服务运行的健壮性,避免级联失败导致的雪崩问题,就属于微服务保护。

1. 微服务保护方案

微服务保护的方案有很多,比如:请求限流、线程隔离、服务熔断等

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

1.1 请求限流

服务故障最重要原因,就是并发太高!解决了这个问题,就能避免大部分故障。当然,接口的并发不是一直很高,而是突发的。因此请求限流,就是限制或控制接口访问的并发流量,避免服务因流量激增而出现故障。

请求限流往往会有一个限流器,数量高低起伏的并发请求曲线,经过限流器就变的非常平稳。这就像是水电站的大坝,起到蓄水的作用,可以通过开关控制水流出的大小,让下游水流始终维持在一个平稳的量。

1.2 线程隔离

为了避免某个接口故障或压力过大导致整个服务不可用,我们可以限定每个接口可以使用的资源范围,也就是将其"隔离"起来。

将不同的服务或模块隔离开来,每个服务或模块拥有独立的资源配额和线程池,防止资源被耗尽。

1.3 服务熔断

在某个服务出现故障时,快速失败而不是等待超时,以防止故障传播。

线程隔离虽然避免了雪崩问题,但故障服务依然会拖慢服务调用方的接口响应速度。

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

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

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

2. 微服务保护实现

微服务保护的技术有很多,但在目前国内使用较多的还是Sentinel。

2.1 Sentinel介绍

Sentinel是阿里巴巴开源的一款服务保护框架,目前已经加入SpringCloudAlibaba中。

官方网站:https://sentinelguard.io/zh-cn/

2.2 Sentinel服务部署

2.2.1 下载

下载jar包,下载地址:https://github.com/alibaba/Sentinel/releases

2.2.2 运行

将jar包放在任意非中文、不包含特殊字符的目录下,重命名为sentinel-dashboard.jar

然后运行如下命令启动控制台:

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

其它启动时可配置参数可参考官方文档:https://github.com/alibaba/Sentinel/wiki/%E5%90%AF%E5%8A%A8%E9%85%8D%E7%BD%AE%E9%A1%B9

2.2.3 访问

访问http://ip:8090(ip替换为部署Sentinel的服务器的ip)页面,就可以看到sentinel的控制台了

需要输入账号和密码,默认都是:sentinel

登录后,即可看到控制台,默认会监控sentinel-dashboard服务本身

2.3 微服务整合

2.3.1 引入依赖

XML 复制代码
<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId> 
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2.3.2 配置

application.yaml文件,添加下面内容:

XML 复制代码
spring:
  cloud: 
    sentinel:
      transport:
        dashboard: ip:8090
      http-method-specify: true # 开启请求方式前缀
    

2.4 请求限流

sentinel控制台,在簇点链路后面点击流控按钮,即可对其做限流配置。

选择阈值类型为:QPS,单机阈值假设为6。

这样就把这个簇点资源的流量限制在了每秒6个,也就是最大QPS为6。

2.5 线程隔离

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

首先,开启Feign的sentinel功能:

XML 复制代码
feign:
  sentinel:
    enabled: true # 开启feign对sentinel的支持

然后,在某个接口A下的,Feign接口调用簇点,后面点击流控按钮,即可对其做隔离配置。

选择阈值类型为:并发线程数,单机阈值假设为6。

这里勾选的是并发线程数限制,也就是说这个Feign接口调用最多使用6个线程,而不是6QPS。

此时如果我们通过页面访问接口A所在服务的其它接口,发现不受影响。这就证明线程隔离起到了作用,尽管接口A并发很高,但是它能使用的线程资源被限制了,因此不会影响到其它接口。

2.6 服务熔断

在上面,我们利用线程隔离对接口A业务进行隔离,保护其它接口。由于接口A下的Feign接口调用服务的耗时较高,再加上线程隔离限定了线程数为6,导致接口吞吐能力有限。这就导致了几个问题:

第一,Feign接口调用超出上限的请求就只能抛出异常,从而导致接口A业务失败。但从业务角度来说,即便Feign接口调用失败,接口A数据也应该展示给用户,用户体验更好。也就是给Feign接口调用失败设置一个降级处理逻辑。

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

2.6.1 降级处理

触发限流或熔断后的请求不一定要直接报错,也可以返回一些默认数据或者友好提示,用户体验会更好。

(1)在Feign模块中给ItemClient定义降级处理类,实现FallbackFactory

java 复制代码
package com.cyt.api.client.fallback;


@Slf4j
public class ItemClientFallback implements FallbackFactory<ItemClient> {
    @Override
    public ItemClient create(Throwable cause) {
        return new ItemClient() {
            @Override
            public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
                log.error("远程调用ItemClient#queryItemByIds方法出现异常,参数:{}", ids, cause);
                // 查询允许失败,查询失败,返回空集合
                return CollUtils.emptyList();
            } 
        };
    }
}

(2)在Feign模块中的com.cyt.api.config.DefaultFeignConfig类中将ItemClientFallback注册为一个Bean

(3)在Feign模块中的ItemClient接口中使用ItemClientFallbackFactory

重启后,再次测试,发现被限流的请求不再报错,走了降级逻辑。

2.6.2 熔断

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

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

状态机包括三个状态:

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

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

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

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

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

我们可以在控制台通过点击簇点后的**熔断** 按钮来配置熔断策略。这里是,接口A下的,Feign接口调用簇点后的**熔断**按钮。

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

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

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

  • 熔断持续时长20s

3. 总结

假设在微服务架构中,有 微服务 A 的接口 A 通过 OpenFeign 远程调用了 微服务 B 的接口 B。为了保证系统的稳定性和可靠性,我们可以通过以下策略来实现对请求的保护和故障隔离:

3.1 请求限流

请求限流 主要作用在接口 A 上,用于限制进入接口 A 的并发请求数量。限流可以设定每秒钟允许的最大请求数,以防止突发流量导致服务过载。比如,如果我们设置接口 A 的限流为每秒最多 100 次请求,当达到上限后,多余的请求会被拒绝或延迟处理,避免接口 A 过载。

3.2 线程隔离

线程隔离 则用于接口 B 的调用。为了防止接口 B 的性能问题影响 微服务 B 内部的其他接口或服务,我们可以为接口 B 配置一个独立的线程池或信号量,限制其并发请求数。例如,将接口 B 的线程池大小设置为 10,可以确保在高并发场景下接口 B 不会占用过多的资源,保护微服务 B 的其他功能模块。这样,即使接口 B 出现性能问题,也能与其他接口实现隔离,避免影响微服务 B 的整体服务能力。

3.3 服务熔断

服务熔断 是一种故障保护机制,当接口 B 的健康状况不佳时,主动中断调用链,以保护调用方 微服务 A 的稳定性。熔断机制通常基于请求的失败率、超时率或慢请求比例等指标。例如,我们可以统计接口 B 的慢请求(即超过设定时间的请求)比例和异常请求(抛出异常的请求)比例,当这些比例超出预设阈值(如,10 秒内错误请求比例超过 50%),就触发熔断。

一旦触发熔断,调用接口 B 的所有请求将被短路,不再真正调用接口 B ,而是直接执行降级逻辑,避免系统资源浪费在不可用的服务上。熔断状态会持续一段时间,直到系统检测到接口 B 的健康状况恢复,才会重新允许请求流入。

3.4 降级处理

在服务熔断触发后,可以执行降级策略 。降级策略可以包括返回默认数据、从缓存中读取数据,或者提供友好的错误提示,确保即使接口 B 不可用,微服务 A 也能继续对外提供服务,改善用户体验。降级处理有助于提升系统的可用性,避免因部分微服务不可用而导致整体服务崩溃。

综上所述,这些微服务保护措施(限流、线程隔离、熔断和降级)共同协作,有助于在高并发和异常情况下保障系统的稳定性和用户体验。其中,限流用于限制接口 A 的流量,线程隔离保证接口 B 的资源独立性,熔断机制快速响应接口 B 的故障,降级策略则提供友好的故障处理机制。

相关推荐
工业甲酰苯胺1 小时前
分布式系统架构:服务容错
数据库·架构
Java程序之猿2 小时前
微服务分布式(一、项目初始化)
分布式·微服务·架构
Yvemil74 小时前
《开启微服务之旅:Spring Boot Web开发举例》(一)
前端·spring boot·微服务
小蜗牛慢慢爬行5 小时前
Hibernate、JPA、Spring DATA JPA、Hibernate 代理和架构
java·架构·hibernate
思忖小下7 小时前
梳理你的思路(从OOP到架构设计)_简介设计模式
设计模式·架构·eit
Yvemil78 小时前
《开启微服务之旅:Spring Boot Web开发》(二)
前端·spring boot·微服务
维李设论8 小时前
Node.js的Web服务在Nacos中的实践
前端·spring cloud·微服务·eureka·nacos·node.js·express
jwolf211 小时前
基于K8S的微服务:一、服务发现,负载均衡测试(附calico网络问题解决)
微服务·kubernetes·服务发现
Yvemil712 小时前
《开启微服务之旅:Spring Boot Web开发举例》(二)
前端·spring boot·微服务