微服务之sentinel限流

课程目标

  • 掌握sentinel的概念

  • 学会使用Sentinel做限流

回顾雪崩

什么是雪崩问题:微服务中,服务间调用关系错综复杂,一个微服务往往依赖于多个其它微服务。如果服务提供者A发生了故障,当前的应用的部分业务因为依赖于服务A,因此也会被阻塞。此时,其它不依赖于服务A的业务似乎不受影响。但是,依赖服务A的业务请求被阻塞,用户不会得到响应,则tomcat的这个线程不会释放,于是越来越多的用户请求到来,越来越多的线程会阻塞.服务器支持的线程和并发数有限,请求一直阻塞,会导致服务器资源耗尽,从而导致所有其它服务都不可用,那么当前服务也就不可用了。

那么,依赖于当前服务的其它服务随着时间的推移,最终也都会变的不可用,形成级联失败,雪崩就发生了。

解决雪崩问题的常见方式有四种:

  • 超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待

  • 线程隔离(仓壁模式):船舱都会被隔板分离为多个独立空间,当船体破损时,只会导致部分空间进入,将故障控制在一定范围内,避免整个船体都被淹没。于此类似,我们可以限定每个业务能使用的线程数,避免耗尽整个tomcat的资源,因此也叫线程隔离。

  • 熔断器(断路器):断路器模式:由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。断路器会统计访问某个服务的请求数量,异常比例,当发现访问服务D的请求异常比例过高时,认为服务D有导致雪崩的风险,会拦截访问服务D的一切请求,形成熔断。

  • 限流:也就是流量控制限制业务访问的QPS,避免服务因流量的突增而故障。

    总结
    什么是雪崩问题?

    微服务之间相互调用,因为调用链中的一个服务故障,引起整个链路都无法访问的情况。
    可以认为:

    1、限流是对服务的保护,避免因瞬间高并发流量而导致服务故障,进而避免雪崩。是一种预防措施。

    2、超时处理、线程隔离、降级熔断是在部分服务故障时,将故障控制在一定范围,避免雪崩。是一种补救措施。

    早期比较流行的是Hystrix框架,但目前国内实用最广泛的还是阿里巴巴的Sentinel框架,这里我们做下对比:
    在这里插入图片描述

1、Sentinel 是什么

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。

2、Sentinel 基本概念

2.1 资源

资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。

只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。

2.2 规则

围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整

3、Sentinel 功能和设计理念

3.1 什么是流量控制

流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:
在这里插入图片描述

3.2 流量控制设计理念

流量控制有以下几个角度:

  • 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
  • 运行指标,例如 QPS、线程池、系统负载等;
  • 控制的效果,例如直接限流、冷启动、排队等。

Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。

4.熔断降级

4.1 什么是熔断降级

除了流量控制以外,及时对调用链路中的不稳定因素进行熔断也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,可能会导致请求发生堆积,进而导致级联错误。

在这里插入图片描述

Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。

4.2 熔断降级设计理念

在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。

Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配。

Sentinel 对这个问题采取了两种手段:

  • 通过并发线程数进行限制

和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。

  • 通过响应时间对资源进行降级

除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。

5、系统自适应保护

Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。

针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

6、Sentinel 特征

  • 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

  • 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

  • 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

•完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

6.1 Sentinel 是如何工作的
  • 对主流框架提供适配或者显示的 API,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。
  • 根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便您定义及改变规则。
  • Sentinel 提供实时的监控系统,方便您快速了解目前系统的状态。
6.2 Sentinel下载

https://github.com/alibaba/Sentinel/releases/tag/v1.8.0

在这里插入图片描述

6.3 启动Sentinel

下载后可以直接运行

java -jar sentinel-dashboard-1.8.0.jar
在这里插入图片描述

看到如下页面说明启动成功,默认端口8080
在这里插入图片描述

浏览器访问http://localhost:8080/看到sentinel登录页面

输入用户名:sentinel,密码:sentinel 登录系统

看到如下页面,左侧属性菜单是空的,默认sentinel是懒加载的,因为没有任何流量访问,所以不会有菜单显示出来,要想看到控制台中的内容,我们需要搭建一个应用程序,访问一下才能有菜单显示出来
在这里插入图片描述
在这里插入图片描述

如果要修改Sentinel的默认端口、账户、密码,可以通过下列配置:

在这里插入图片描述

6.4 搭建一个Springboot程序(provider-service)

pom.xml文件增加如下依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.summer</groupId>
        <artifactId>alibaba-server</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>provider-service</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!--            spring boot依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--        注册中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--        负载均衡依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <!--配置文件改为bootstrap.yml时所需依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
<!--        sentinel依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
    </dependencies>
</project>

编写启动类

@SpringBootApplication
@EnableDiscoveryClient // 开启注册中心客户端
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class,args);
    }
}

编写一个controller类

package com.summer.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author Echo
 * @version 1.0
 * @date 2023/8/31 19:08
 */
@RequestMapping("/order")
public class OrderController {
    @GetMapping("/query/{id}")
    public String queryOrder(@PathVariable("id") Long id) {
        return "查询订单成功->id为:" + id;
    }

    @GetMapping("/update/{id}")
    public String updateOrder(@PathVariable("id") Long id) {
        return "更新订单成功id为:" + id;
    }

}

bootstrap.yml中增加如下内容

server:
  port: 8081
spring:
  application:
    name: provider-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #注册中心地址
        namespace: 7c1747ff-bc39-4903-b6bb-a3eefd5fdabc # 命名空间
        group: DEV_GROUP # 群组
      config:
        server-addr: localhost:8848 #配置中心地址
        group: DEV_GROUP # 群组
        namespace: 7c1747ff-bc39-4903-b6bb-a3eefd5fdabc # 命名空间
        file-extension: yml # 配置文件拓展名
    sentinel:
      transport:
        dashboard: localhost:8080

访问provider-service的任意端点

启动程序打开浏览器并访问一下接口http://localhost:8081/order/query/1,这样才能触发sentinel的监控

然后再访问sentinel的控制台,查看效果:就会发现provider-service这个微服务名称已经出现在左侧树形菜单中。

在这里插入图片描述

6.5 菜单介绍
  • 实时监控:Sentinel 提供对所有资源的实时监控。
  • 簇点链路:对资源(接口)调用的详细统计。
  • 流控规则:配置对资源的流量控制规则。
  • 降级规则:配置资源的熔断降级规则。
  • 热点规则:配置某些热点资源的流控规则。
  • 系统规则:整个系统的流控规则。
  • 授权规则:对某个资源访问的黑白名单规则。
  • 集群流控:配置集群的限流规则。
  • 机器列表:微服务的机器列表。
6.6 功能演示
6.6.1 实时监控

当我们访问应用程序的接口时候,比如连续访问http://localhost:8081/order/query/1,这个页面上会显示出曲线图以及统计表格。

在这里插入图片描述

6.6.2 Sentinel限流-QPS限流

簇点链路

当请求进入微服务时,首先会访问DispatcherServlet,然后进入Controller、Service、Mapper,这样的一个调用链就叫做簇点链路。簇点链路中被监控的每一个接口就是一个资源。

默认情况下sentinel会监控每一个端点(controller中的方法),因此每一个端点就是调用链路中的一个资源。
在这里插入图片描述

每一个可以访问的资源都列在这里,并且表格中也可以看出一些统计信息。操作一栏中流控降级热点授权这四个按钮跟左侧树形菜单中的流控规则降级规则热点规则授权规则点击进入的是相同功能。

  • 流控:流量控制
  • 降级:降级熔断
  • 热点:热点参数限流,是限流的一种
  • 授权:请求的权限控制

这几个功能也是我们重点要演示的。

点击order/query/1右侧的流控按钮可以看到如下流控配置页面,表单中可以填写限流规则

在这里插入图片描述

在这里插入图片描述

  • 资源名:唯一名称,默认请求路径
  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,指定对哪个微服务进行限流 ,默认default(不区分来源,全部限制)
  • 阈值类型/单机阈值:
    1、QPS(每秒钟的请求数量):当调用该接口的QPS达到了阈值的时候,进行限流;
    2、线程数:当调用该接口的线程数达到阈值时,进行限流
  • 是否集群:不需要集群
  • 流控模式:
    1、直接:接口达到限流条件时,直接限流
    2、关联:当关联的资源达到阈值时,就限流自己
    3、链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就可以限流)[api级别的针对来源]
  • 流控效果
    1、快速失败:直接失败
    2、Warm Up:即请求 QPS 从 threshold / 3开始,经预热时长逐渐升至设定的 QPS 阈值
    3、排队等待

演示1:Sentinel限流-流控模式-直接模式: 统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式

流控规则: 其含义是限制 order/query/1这个资源的单机QPS为2,即每秒只允许2次请求,超出的请求会被拦截并报错。

我们点击新增按钮,添加这条流控规则.

在这里插入图片描述

我们通过浏览器访问order/query/1这个接口,使用鼠标快速点击,当点击过快的时候,超过了QPS=2,那么会看到Sentinel做出了限流响应。

在这里插入图片描述

演示2: Sentinel限流-流控模式-关联模式: 统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流

流控规则: 当/order/update/{id}资源被访问的QPS超过5时,对/order/query/{id}请求限流

使用场景: 比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是优先支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。

在这里插入图片描述

对哪个端点限流,就点击哪个端点后面的按钮。我们是对订单查询/order/query{id}限流,因此点击它后面的按钮:

在这里插入图片描述

Jmeter测试

在这里插入图片描述

我们请求的目标是/order/update/1,这样这个断点就会触发阈值。但限流的目标是/order/query/1,我们在浏览器访问,会出现如下被限流的返回信息,确实被限流了。

在这里插入图片描述

演示3:Sentinel限流-流控模式-链路模式: 统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流.

流控规则: 限制queryGoods的QPS为2,只限制从/order/query进入的请求

使用场景: 有查询订单和创建订单业务,两者都需要查询商品。针对从查询订单进入到查询商品的请求统计,并设置限流。

默认情况下,OrderService中的方法是不被Sentinel监控的,需要我们自己通过注解来标记要监控的方法。

给OrderService的queryGoods方法添加@SentinelResource注解:

package com.summer.controller;

import com.summer.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Echo
 * @version 1.0
 * @date 2023/8/31 19:08
 */
@RestController
@RefreshScope
@RequestMapping("/order")
public class OrderController {

    @Autowired
    OrderService orderService;

    /**
     * 查询订单
     * @return
     */
    @GetMapping("/query")
    public String queryOrder() {
        // 商品查询
        orderService.queryGoods();
        return orderService.queryOrder();
    }
    /**
     * 更新订单
     * @return
     */
    @GetMapping("/update")
    public String updateOrder() {
        return orderService.updateOrder();
    }
    /**
     * 新增订单
     * @return
     */
    @GetMapping("/create")
    public String createOrder() {
        // 商品查询
        orderService.queryGoods();
        return orderService.createOrder();
    }
    /**
     * 查询商品
     * @return
     */
    @GetMapping("/queryGoods")
    public String queryGoods() {
        return orderService.queryGoods();
    }

}
  @SentinelResource("goods")
    public String queryGoods() {
        return "查询商品成功";
    }

链路模式中,是对不同来源的两个链路做监控。但是sentinel默认会给进入SpringMVC的所有请求设置同一个root资源,会导致链路模式失效。

在这里插入图片描述

添加流控规则: 只统计从/order/query进入/goods的资源,QPS阈值为1,超出则被限流。

在这里插入图片描述

在这里插入图片描述

可以看到这里200个用户,50秒内发完,QPS为4,超过了我们设定的阈值1.

在这里插入图片描述

每次只有1个通过

在这里插入图片描述

流控效果是指请求达到流控阈值时应该采取的措

演示4:Sentinel限流-流控效果-warm up: 预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。

warm up说明: 阈值一般是一个微服务能承担的最大QPS,但是一个服务刚刚启动时,一切资源尚未初始化(冷启动),如果直接将QPS跑到最大值,可能导致服务瞬间宕机。warm up也叫预热模式,是应对服务冷启动的一种方案。请求阈值初始值是 maxThreshold / coldFactor,持续指定时长后,逐渐提高到maxThreshold值。而coldFactor的默认值是3.

例如,我设置QPS的maxThreshold为10,预热时间为5秒,那么初始阈值就是 10 / 3 ,也就是3,然后在5秒后逐渐增长到10.

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

刚启动时,大部分请求失败,成功的只有3个,说明QPS被限定在3.

在这里插入图片描述

随着时间推移,成功率越来越高.

在这里插入图片描述

到Sentinel控制台查看/order/query的实时监控

在这里插入图片描述

一段时间后,发现通过的QPS变多了

在这里插入图片描述

演示5:Sentinel限流-流控效果-排队等待: 让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长

排队等待说明: 当请求超过QPS阈值时,快速失败和warm up 会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。

在这里插入图片描述

在这里插入图片描述

QPS为15,已经超过了我们设定的10。

在这里插入图片描述

如果是之前的 快速失败、warmup模式,超出的请求应该会直接报错。但是我们看看运行结果,全部都通过了.

再去sentinel查看实时监控的QPS曲线,非常平滑,一致保持在10,但是超出的请求没有被拒绝,而是放入队列。因此响应时间(等待时间)会越来越长。

在这里插入图片描述

当队列满了以后,才会有部分请求失败

在这里插入图片描述

演示6:Sentinel限流-热点参数限流-全局参数:之前的限流是统计访问某个资源的所有请求,判断是否超过QPS阈值。而热点参数限流是分别统计参数值相同的请求,判断是否超过QPS阈值。

全局参数限流:例如,一个根据id查询商品的接口,访问/order/query/goods/{id}的请求中,id参数值会有变化,热点参数限流会根据参数值分别统计QPS.假如我们限制id=1的请求触发阈值被限流,那么id值不为1的请求不受影响。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

我们配置的QPS=10触发阈值,从运行结果看出超过5个被限流了.

热点参数限流说明: 刚才的配置中,对查询商品这个接口的所有商品一视同仁,QPS都限定为5.而在实际开发中,可能部分商品是热点商品,例如双11或者618秒杀商品,我们希望这部分商品的QPS限制与其它商品不一样,高一些。那就需要配置热点参数限流的高级选项进行控制了.

在这里插入图片描述

结合上一个配置,这里的含义是对0号的int类型参数限流,每1秒相同参数的QPS不能超过5,但是有两个例外:

•如果参数值是6,则每1秒允许的QPS为2.

在这里插入图片描述

在这里插入图片描述

•如果参数值是8,则每1秒允许的QPS为3.

在这里插入图片描述

在这里插入图片描述

7、Sentinel隔离降级-Feign整合Sentinel

SpringCloud中,微服务调用都是通过Feign来实现的,因此做客户端保护必须整合Feign和Sentinel。

  • 修改配置,开启sentinel功能

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

在这里插入图片描述

对应服务配置限流规则

在这里插入图片描述

我们直接访问服务被限流后返回的是sentinel的默认异常.

在这里插入图片描述

通过feign调用被限流后返回fallback的配置信息.

在这里插入图片描述

8、Sentinel隔离降级-线程隔离

线程数:是该资源能使用用的tomcat线程数的最大值。也就是通过限制线程数量,实现线程隔离(舱壁模式)。

使用场景: 给 order-service服务中的feign调用根据商品名称查询接口/order/queryGoodsByName设置流控规则,线程数不能超过 2,然后利用jemeter测试。

在这里插入图片描述

在这里插入图片描述

0s内我们发送40个请求,有较大概率并发线程数超过2,而超出的请求会走之前定义的失败降级逻辑。

在这里插入图片描述

在这里插入图片描述

当Jmeter访问接口的时,发现虽然结果都是通过了,不过部分请求得到的响应是降级返回fallback信息.

9、Sentinel隔离降级-熔断降级

熔断降级说明:熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。

  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
字段说明默认值
resource资源名,即规则的作用对象
grade熔断策略,支持慢调用比例/异常比例/异常数策略慢调用比例
count慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow熔断时长,单位为 s
minRequestAmount熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入)5
statIntervalMs统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)1000 ms
slowRatioThreshold慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)

我们给OrderController的查询订单详情接口设置降级规则,慢调用的RT阈值为500ms,最小请求数量为5,失败阈值比例为0.5,熔断时长为5.

在这里插入图片描述

解读:RT超过50ms的调用是慢调用,统计请求,如果请求量超过5次,并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。

通过休眠模拟一个延迟时间

    /** 
     * controller
     * 查询订单
     * @return
     */
    @GetMapping("/queryOrderDetail")
    public String queryOrderDetail(Integer id) throws InterruptedException {
        return orderService.queryOrderDetail(id);
    }

	// service
   public String queryOrderDetail(Integer id) throws InterruptedException {
        if(id==1){
            Thread.sleep(60);
        }

        return "查询订单详情成功";
    }

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

导致id=3的也被熔断了

在这里插入图片描述

10、异常比例、异常数

异常比例或异常数说明:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。

在这里插入图片描述

统计如果请求量超过5次,并且异常比例不低于0.5,则触发熔断.

在这里插入图片描述

在这里插入图片描述

统计如果请求量超过5次,并且异常比例不低于2次,则触发熔断.

在这里插入图片描述

11、Sentinel访问授权-微服务访问授权

11.1 基本规则

授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。

  • 白名单:来源(origin)在白名单内的调用者允许访问
  • 黑名单:来源(origin)在黑名单内的调用者不允许访问

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

Sentinel提供了 RequestOriginParser 接口来处理访问来源,Sentinel保护的资源如果被访问,就会调用 RequestOriginParser解析访问来源。

11.2 如何获取origin
public interface RequestOriginParser {
    /**
     * 从请求request对象中获取origin,获取方式自定义
     */
    String parseOrigin(HttpServletRequest request);
}

这个方法的作用就是从request对象中,获取请求者的origin值并返回。默认情况下,sentinel不管请求者从哪里来,返回值永远是default,也就是说一切请求的来源都被认为是一样的值default。因此,我们需要自定义这个接口的实现,让不同的请求,返回不同的origin。

/**
 * @author Echo
 * @version 1.0
 * @date 2023/9/1 19:42
 */

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

@Component
public class HeaderOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
        // 1.获取请求头
        String origin = request.getHeader("origin");
        // 2.非空判断
        if (StringUtils.isEmpty(origin)) {
            origin = "blank";
        }
        return origin;
    }
}
server:
  port: 6060
spring:
  application:
    name: gateway-service
  profiles:
    active: dev
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #注册中心地址
        namespace: 7c1747ff-bc39-4903-b6bb-a3eefd5fdabc # 命名空间
        group: DEV_GROUP # 群组
      config:
        server-addr: localhost:8848 #配置中心地址
        group: DEV_GROUP # 群组
        namespace: 7c1747ff-bc39-4903-b6bb-a3eefd5fdabc # 命名空间
        file-extension: yml # 配置文件拓展名
    gateway:
      default-filters:
        - AddRequestHeader=origin,gateway #给网关添加请求头
      routes:
        - id: consumer-service
          uri: lb://consumer-service
          predicates:
            - Query=green
            - Path=/gateway/consumer/**
          filters:
            - StripPrefix=1
        - id: provider-service
          uri: lb://provider-service
          predicates:
            - Path=/gateway/provider/**,/gateway/order/**
#            - Query=red  # 我们的请求参数里带有red的请求 且 路径能匹配到/provider/ 就转发到http://localhost:8080
          filters:
            - StripPrefix=1
request:
  whites:
    - /gateway/consumer/**
    - /gateway/feign/**
    - /gateway/order/**

在OrderController中新增测试授权接口/testAuthorize

    /**
     * 测试授权
     * @return
     */
    @GetMapping("/testAuthorize")
    public String testAuthorize() {
        return "授权通过";
    }

添加授权规则

在这里插入图片描述

直接访问此时已经访问不通了,说明被授权规则限制.

在这里插入图片描述

通过网关访问授权通过.
在这里插入图片描述

反之若把gateway设置为黑名单,则网关访问不通过,直接访问服务通过.

12、Sentinel自定义异常结果信息

如果要自定义异常时的返回结果,需要实现BlockExceptionHandler接口

public interface BlockExceptionHandler {
    /**
     * 处理请求被限流、降级、授权拦截时抛出的异常:BlockException
     */
    void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
}

这个方法有三个参数:

  • HttpServletRequest request:request对象
  • HttpServletResponse response:response对象
  • BlockException e:被sentinel拦截时抛出的异常

BlockException包含多个不同的子类:

在这里插入图片描述

自定义异常处理

我们在provider-service里自定义一个sentinel的异常处理

package com.summer.exception;

/**
 * @author Echo
 * @version 1.0
 * @date 2023/9/1 20:11
 */
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        String msg = "未知异常";
        int status = 429;
        if (e instanceof FlowException) {
            msg = "请求被限流";
        } else if (e instanceof ParamFlowException) {
            msg = "请求被热点参数限流";
        } else if (e instanceof DegradeException) {
            msg = "请求被降级";
        } else if (e instanceof AuthorityException) {
            status = 401;
            msg = "无权限访问";
        }
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(status);
        response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");
    }
}


在这里插入图片描述

13、集群流控

为什么要使用集群流控呢?假设我们希望给某个用户限制调用某个 API 的总 QPS 为 50,但机器数可能很多(比如有 100 台)。这时候我们很自然地就想到,找一个 server 来专门来统计总的调用量,其它的实例都与这台 server 通信来判断是否可以调用。这就是最基础的集群流控的方式。

另外集群流控还可以解决流量不均匀导致总体限流效果不佳的问题。假设集群中有 10 台机器,我们给每台机器设置单机限流阈值为 10 QPS,理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。

集群流控中共有两种身份:

  • Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。

  • Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。

再搭建一个跟provider-service一样的服务,并且注册到nacos中,这样两个provider-service就组成一个集群。在Sentinel中可以看到目前我们provider-service有两个实例

在这里插入图片描述

添加一个Token Server

在这里插入图片描述

添加Token Server ,Token Client,设置集群QPS

在这里插入图片描述

1、资源名指的就是需要进行流量限制的接口名

2、针对来源是可以对调用该接口的来源进行限流配置,默认default全部

3、阈值类型有 qps和并发线程数,我们这里选qps,集群阈值填1,表示1s只能有一个请求,集群阈值模式可以选单机均摊和总体阈值,单机均摊会根据服务的总连接数计算总的阈值,而总体阈值是针对于整个集群而言,这里我们选总体阈值,失败退化是指token服务不可用之后自动转换成单机限流。

配置好规则后,通过浏览器访问 http://localhost:6060/gateway/order/testCollect 这个是provider-service的接口

当访问速度很慢的时候,可以正常返回

如果快速点击刷新按钮,会报错,看到限流效果.

在这里插入图片描述

14、系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

系统规则支持以下的模式:

  • Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
  • 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

15、机器列表

我们的provider-service有两个微服务实例,目前可以在Sentinel控制台看到,点击机器列表菜单,可以在列表中看到我们两个实例的列表
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值