微服务架构 --- 使用Sentinel来处理请求限流+线程隔离+服务熔断

目录

一.什么是Sentinel?

二.Sentinel的核心概念:

三.Sentinel的使用:

1.在本地运行Sentinel控制台:

2.在微服务模块导入依赖以及配置文件:

(1)导入依赖:

(2)配置文件:

(3)访问模块的任意端点:

什么是簇点链路?

[1. 簇点(Cluster Head)](#1. 簇点(Cluster Head))

[2. 簇点链路](#2. 簇点链路)

四.请求限流:

[五. 线程隔离:](#五. 线程隔离:)

1.整合Sentinel:

2.配置线程隔离:

六.服务熔断:

1.降级处理:

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

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

(3)在hm-api模块中的ItemClient接口中使用ItemClientFallbackFactory:

2.服务熔断:


一.什么是Sentinel?

Sentinel 是阿里巴巴开源的面向分布式系统的流量防护组件,专注于流量控制熔断降级系统负载保护 等。在微服务架构中,合理使用 Sentinel,可以避免单个服务故障引发系统级崩溃。本文详细讲解如何使用 Sentinel 实现线程隔离服务熔断

二.Sentinel的核心概念:

  • 线程隔离

    通过将某些耗时、可能阻塞的操作放到独立线程池中执行,避免因为某个任务阻塞而影响主线程或其他服务调用。

  • 熔断降级

    当系统检测到请求失败率过高或响应时间过长时,自动开启熔断机制,暂时阻断某些请求,避免系统崩溃。

  • 限流

    控制流量的输入速率,防止因流量过大导致系统崩溃。

三.Sentinel的使用:

1.在本地运行Sentinel控制台:

下面是官网:https://github.com/alibaba/Sentinel/releases

在官网下载Sentinel Dashboard,随后运行下面命令启动控制台:

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

复制代码
java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
  • 打开浏览器访问:http://localhost:8090
  • 选择对应的服务进行限流、熔断等规则配置。

2.在微服务模块导入依赖以及配置文件:

我们在模块中整合sentinel,连接sentinel-dashboard控制台

(1)导入依赖:

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

(2)配置文件:

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

XML 复制代码
spring:
  cloud: 
    sentinel:
      transport:
        dashboard: localhost:8090

(3)访问模块的任意端点:

访问查询购物车接口,sentinel的客户端就会将服务访问的信息提交到sentinel-dashboard控制台。

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

什么是簇点链路?
1. 簇点(Cluster Head)
  • 网络中的节点被划分为多个簇,每个簇中选出一个节点作为簇头(簇点)。簇头负责管理簇内的通信,并与其他簇头通信或与基站通信。
  • 簇头节点通常比普通节点有更多的计算和通信能力,起到数据汇聚、路由的作用。
2. 簇点链路
  • 簇点链路是指簇头节点之间的通信链路,这些链路通过无线电通信方式建立,簇头通过这些链路传递簇内数据到其他簇或中央节点。
  • 簇点链路使得数据能够从一个簇传递到另一个簇,最终到达数据汇聚中心(如基站或控制节点)。

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

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

不过,需要注意的是,我们的SpringMVC接口是按照Restful风格设计,因此购物车的查询、删除、修改等接口全部都是/carts路径:

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

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

首先,在cart-serviceapplication.yml中添加下面的配置:

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

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

四.请求限流:

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

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

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

在弹出的菜单中这样填写:

  • 资源名

    • 指定需要限流的资源,比如该配置中的 GET:/carts,表示针对 GET 请求访问 /carts 资源时设置限流规则。
  • 针对来源

    • 表示限流策略应用于哪个调用者来源。默认是 default,表示对所有调用者生效。可以根据业务需求配置不同来源的限流规则。
  • 阈值类型

    • QPS :表示每秒查询次数(Queries Per Second)。如果选择 QPS,表示设置的限流规则是基于每秒允许的最大请求数量。
    • 并发线程数 :如果选择该选项,表示限制的是同一时间内并发执行的线程数量。
    • 本例中选择了 QPS ,并设置了单机阈值为 6,意味着每秒最多允许 6 次请求通过该资源。
  • 单机阈值

    • 这是针对单台机器的限流阈值。在此配置中,设置为 6,表示每秒最多处理 6 个请求。如果请求数量超过 6 个,将触发限流策略。
  • 是否集群

    • 用于确定限流规则是否针对集群中的多个节点进行一致的限流。如果勾选此选项,意味着整个集群将共享同一限流规则。
    • 此选项没有被勾选,表示当前规则只针对单机生效。

五. 线程隔离:

当一个业务接口响应时间长,而且并发高时,就可能耗尽服务器的线程资源,导致服务内的其它接口受到影响。所以我们必须把这种影响降低,或者缩减影响的范围。线程隔离正是解决这个问题的好办法。为了避免某个接口故障或压力过大导致整个服务不可用,我们可以限定每个接口可以使用的资源范围,也就是将其"隔离"起来。

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

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

如图所示,我们给查询购物车业务限定可用线程数量上限为20,这样即便查询购物车的请求因为查询商品服务而出现故障,也不会导致服务器的线程资源被耗尽,不会影响到其它接口。这样,即便商品服务出现故障,最多导致查询购物车业务故障,并且可用的线程资源也被限定在一定范围,不会导致整个购物车服务崩溃。

所以,我们要对查询商品的FeignClient接口做线程隔离。在上面文章讲述了OpenFeign的使用,为我们提到使用FeignClient来进行远程调用。所以需要给OpenFeign整合Sentinel进行线程隔离。

1.整合Sentinel:

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

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

需要注意的是,默认情况下SpringBoot项目的tomcat最大线程数是200,允许的最大连接是8492,单机测试很难打满。所以我们需要配置一下cart-service模块的application.yml文件,修改tomcat连接:(开发可以不用配置这步对tomcat的限制)

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

然后重启cart-service服务,可以看到查询商品的FeignClient自动变成了一个簇点资源:

2.配置线程隔离:

接下来,点击查询商品的FeignClient对应的簇点资源后面的流控按钮:

在弹出的表单中填写下面内容:

因为需要分配线程,所以在这步需要选并发线程数,以此来设定该接口最多使用5个线程。如果查询商品的接口每秒处理2个请求,则5个线程的实际QPS在10左右,而超出的请求自然会被拒绝。

六.服务熔断:

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

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

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

1.降级处理:

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

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

给FeignClient编写失败后的降级逻辑有两种方式:

  • 方式一:FallbackClass,无法对远程调用的异常做处理

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

这里讲述第二种方式:

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

在client/fallback包下创建ItemClientFallback:

java 复制代码
@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();
            }

            @Override
            public void deductStock(List<OrderDetailDTO> items) {
                // 库存扣减业务需要触发事务回滚,查询失败,抛出异常
                throw new BizIllegalException(cause);
            }
        };
    }
}

这里展示一下ItemClient的代码:

java 复制代码
@FeignClient("item-service")
public interface ItemClient {

    @GetMapping("/item")
    List<ItemDTO> queryItemByIds(@RequestParam("Ids") Collection<Long> ids);
}

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

java 复制代码
//定义日志级别(FULL级)
public class DefaultFeignConfig {
    @Bean
    public Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }

    @Bean
    public RequestInterceptor userInfoRequestInterceptor(){
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate requestTemplate) {
                Long userId = UserContext.getUser();
                if(userId != null){
                    requestTemplate.header("user-info",userId.toString());
                }
            }
        };
    }

    @Bean
    public ItemClientFallback itemClientFallback(){
        return new ItemClientFallback();
    }
}

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

java 复制代码
@FeignClient(value = "item-service",
            configuration = DefaultFeignConfig.class,
            fallbackFactory = ItemClientFallback.class)
public interface ItemClient {

    @GetMapping("/item")
    List<ItemDTO> queryItemByIds(@RequestParam("Ids") Collection<Long> ids);
}
  • value = "item-service": 这表示要调用的微服务的名称为 item-service。Feign 会使用这个名称来查找和调用对应的服务。
  • configuration = DefaultFeignConfig.class: 这表示使用 DefaultFeignConfig 类中的配置来配置 Feign Client。此配置类可以定义 Feign 的一些自定义设置,如超时、编码、日志级别等。
  • fallbackFactory = ItemClientFallback.class: 这表示使用 ItemClientFallback 类作为熔断器的工厂。该类用于处理当 Feign 调用失败时的回退逻辑。

2.服务熔断:

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

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

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

状态机包括三个状态:

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

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

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

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

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

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

在弹出的表格中这样填写:

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

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

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

  • 熔断持续时长20s

相关推荐
蝎子莱莱爱打怪3 天前
XZLL-IM干货系列 04|Netty 长连接实战:Pipeline 怎么排、心跳怎么跳、连接怎么管
后端·微服务·面试
SamDeepThinking4 天前
Java微服务练习方式
java·后端·微服务
米丘7 天前
微前端之 Web Components 完全指南
微服务·html
霸道流氓气质10 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
霸道流氓气质10 天前
Spring Boot 微服务性能优化完全指南
spring boot·微服务·性能优化
地瓜伯伯10 天前
从MESI缓存一致性协议讲透synchronized的底层
java·spring boot·spring·spring cloud·微服务·springcloud
Devin~Y10 天前
大厂 Java 面试实录:从音视频内容社区到 AI RAG 的全链路技术设计
java·spring boot·redis·spring cloud·微服务·kafka·音视频
递归尽头是星辰10 天前
AI 访问数据仓库:从直连到微服务化
数据仓库·人工智能·微服务·dataagent·ai数据治理
就改了11 天前
Windows 环境 SkyWalking 完整实操教程
windows·微服务·skywalking
至乐活着11 天前
Docker Compose多服务编排实战:从零搭建Node.js+MySQL+Redis全栈应用
docker·微服务·devops·容器编排·compose