likes
comments
collection
share

SpringCloud Alibaba Sentinel实现熔断与限流

作者站长头像
站长
· 阅读数 16

安装Sentinel控制台

github.com/alibaba/Sen…

SpringCloud Alibaba Sentinel实现熔断与限流

点击选择版本,进入下载页面,页面最下方含有下载连接。点击下载

SpringCloud Alibaba Sentinel实现熔断与限流

改控制台是由SpringBoot编写,内嵌tomcat。下载完成后保证java8环境OK,8080端口不能被占用

点击或者使用java -jar命令运行即可

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

启动成功后访问http://localhost:8080

SpringCloud Alibaba Sentinel实现熔断与限流

登录账号密码均为sentinel

SpringCloud Alibaba Sentinel实现熔断与限流

环境搭建

创建模块cloudalibaba-sentinel-service8401

SpringCloud Alibaba Sentinel实现熔断与限流

pom

<dependencies>
    <!--sentinel-datasource-nacos持久化-->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    <!--sentinel-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <!--nacos-discovery-->
    <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-openfeign</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>4.6.3</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

</dependencies>

yml

SpringCloud Alibaba Sentinel实现熔断与限流

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        #Nacos 服务注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        #配置Sentinel dashboard
        dashboard: localhost:8080
        #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
        port: 8719

management:
  endpoints:
    web:
      exposure:
        include: '*'

主启动类

创建MainApp8401

SpringCloud Alibaba Sentinel实现熔断与限流

@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {
    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class, args);
    }
}

controller

创建FlowLimitController

SpringCloud Alibaba Sentinel实现熔断与限流

@RestController
public class FlowLimitController {
    @GetMapping("/testA")
    public String testA() {
        return "------testA";
    }

    @GetMapping("/testB")
    public String testB() {

        return "------testB";
    }


}

测试

启动Sentinel8080 Nacos Server 8848 启动微服务8401 启动8401微服务后查看sentienl控制台http://localhost:8080/#/dashboard

SpringCloud Alibaba Sentinel实现熔断与限流

空空如也,啥都没有,这是因为Sentinel采用的懒加载说明 我们需要先执行一次访问即可http://localhost:8401/testA

SpringCloud Alibaba Sentinel实现熔断与限流

http://localhost:8401/testB

SpringCloud Alibaba Sentinel实现熔断与限流

此时便会出现页面效果

SpringCloud Alibaba Sentinel实现熔断与限流

sentinel8080正在监控微服务8401!

流控规则

github.com/alibaba/Sen…

基本介绍

资源名:唯一名称,默认请求路径

针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)

阈值类型/单机阈值

  • QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流
  • 线程数:当调用该api的线程数达到阈值的时候,进行限流

是否集群:不需要集群

流控模式:

  • 直接:api达到限流条件时,直接限流
  • 关联:当关联的资源达到阈值时,就限流自己
  • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】

流控效果:

  • 快速失败:直接失败,抛异常
  • Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值coderFactor,经过预热时长,才达到设置的QPS阈值

添加方式。可以在簇点链路添加,也可以在流控规则中添加

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

流控模式

直接

QPS

/testA新增流控规则,流控模式为直接,流控效果为快速失败,阈值类型为QPS 单机阈值为1

SpringCloud Alibaba Sentinel实现熔断与限流

添加完成后可以流控规则中查看

SpringCloud Alibaba Sentinel实现熔断与限流

此时的阈值是1,也就是每秒钟最多访问一次。进行测试http://localhost:8401/testA

SpringCloud Alibaba Sentinel实现熔断与限流

当超过阈值报错Blocked by Sentinel (flow limiting),这就是直接快速失败,系统默认。

线程数

修改/testA流控规则,流控模式为直接,流控效果为快速失败,阈值类型为线程数 单机阈值为1

SpringCloud Alibaba Sentinel实现熔断与限流

线程数:当调用该api的线程数达到阈值的时候,进行限流。也就是有超过一个个线程在访问/testA进行限流,为了看到效果,修改一些controller代码

SpringCloud Alibaba Sentinel实现熔断与限流

@RestController
public class FlowLimitController {
    @GetMapping("/testA")
    public String testA() {
        //暂停
        try {
            TimeUnit.MILLISECONDS.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "------testA";
    }

    @GetMapping("/testB")
    public String testB() {

        return "------testB";
    }


}

重新启动8401,打开两个页面访问http://localhost:8401/testA,当线程数超过两个的时候触发了快速失败的流控效果,为一个的时候正常访问

SpringCloud Alibaba Sentinel实现熔断与限流

直接调用默认报错信息,技术方面OK ,但是是否应该有我们自己的后续处理?

关联

当关联的资源达到阈值时,就限流自己,当与A关联的资源B达到阈值后,就限流自己

配置A流控规则,关联资源/testB 阈值类型为QPS 单机阈值为1,线程停止1秒注释

SpringCloud Alibaba Sentinel实现熔断与限流

postman模拟并发密集访问testB,启动postman

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

访问testB期间,访问http://localhost:8401/testA,testA出现限流

SpringCloud Alibaba Sentinel实现熔断与限流

当对testB的测试完成后

SpringCloud Alibaba Sentinel实现熔断与限流

再次访问http://localhost:8401/testA,访问正常

SpringCloud Alibaba Sentinel实现熔断与限流

链路

多个请求调用了同一个微服务。只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】

SpringCloud Alibaba Sentinel实现熔断与限流

修改流控规则

SpringCloud Alibaba Sentinel实现熔断与限流

当我们多次点击超过QPS阈值时,报错

SpringCloud Alibaba Sentinel实现熔断与限流

流控效果

快速失败

直接失败限流,抛出异常 源码com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

SpringCloud Alibaba Sentinel实现熔断与限流

Warm Up(预热)

阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值

默认coldFactor为3,即请求QPS从threshold(阈值)/3开始,经预热时长逐渐升至设定的QPS阈值

当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等

SpringCloud Alibaba Sentinel实现熔断与限流

com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

SpringCloud Alibaba Sentinel实现熔断与限流

配置流控规则

SpringCloud Alibaba Sentinel实现熔断与限流

此配置系统初始的阈值为 10/3 约等于3,即阈值刚开始为3,然后在这5秒内阈值慢慢上升至10

测试访问http://localhost:8401/testB,当我们不停访问的时候

SpringCloud Alibaba Sentinel实现熔断与限流

前面几秒它的阈值是3慢慢上升的,我们快速点击访问超过了它的阈值发生限流,随着后面阈值慢慢上升,我们的的一秒内访问的次数已经小于它的阈值了,所以限流次数也就慢慢减少,经过5秒它的阈值就变成了10.此时只有一秒钟访问10次以上才能触发限流。

应用场景如秒杀系统在开启的瞬间,会有很多流量上来,很有可能吧系统打死,预热方式就是为了保护系统,慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值。

排队等待

匀速排队,阈值必须设置为QPS,也只能设置为QPS

匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 流量控制 - 匀速器模式,具体的例子可以参见 PaceFlowDemo

SpringCloud Alibaba Sentinel实现熔断与限流

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

SpringCloud Alibaba Sentinel实现熔断与限流

/testA每秒1次请求,超过的化就排队等待,等待的超时时间为20000毫秒

修改一下controller,打印日志

SpringCloud Alibaba Sentinel实现熔断与限流

@RestController
@Slf4j
public class FlowLimitController {
    @GetMapping("/testA")
    public String testA() {
        //暂停
        //try {
        //    TimeUnit.MILLISECONDS.sleep(1000);
        //} catch (InterruptedException e) {
        //    e.printStackTrace();
        //}
        return "------testA";
    }

    @GetMapping("/testB")
    public String testB() {
        log.info(Thread.currentThread().getName()+"\t"+"....testB");
        return "------testB";
    }


}

SpringCloud Alibaba Sentinel实现熔断与限流

查看控制台打印效果

SpringCloud Alibaba Sentinel实现熔断与限流

可以看见以1ms的访问速度,却在控制台是以一秒的速度排队输出打印语句的,说明我们配置的排队等待生效了!

降级规则

github.com/alibaba/Sen…

基本介绍

SpringCloud Alibaba Sentinel实现熔断与限流

RT(平均响应时间。秒级)

  • 平均响应时间 超出阈值 且 在时间窗口内通过的请求>=5,两个条件同时满足后触发降级
  • 窗口期过后关闭断路器
  • RT最大4900(更大的需要通过 -Dcsp.sentinel.statistic.max.rt=xxx配置生效)

异常比例(秒级)

  • QPS >= 5 且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

异常数(分钟级)

  • 异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

Sentinel的断路器是没有半开状态的,半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。具体可以参考Hystrix

RT

平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 N 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。

SpringCloud Alibaba Sentinel实现熔断与限流

controller中添加代码,重启

SpringCloud Alibaba Sentinel实现熔断与限流

@GetMapping("/testD")
public String testD() {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.info("testD 测试RT");

    return "------testD";
}

SpringCloud Alibaba Sentinel实现熔断与限流

Jmeter测试

SpringCloud Alibaba Sentinel实现熔断与限流

此时http://localhost:8401/testD

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

按照上述配置,永远一秒钟打进来10个线程(大于5个)调用testD,RT为200毫秒,但是testD中又睡眠了1秒,所以200毫秒RT中是处理不完的。在未来的1秒钟的时间窗口内,断路器打开(保险跳闸)微服务不可用,保险丝跳闸断电。后续停止Jmeter,没有每秒10个线程的访问量,不再大于5,断路器关闭(保险丝恢复)微服务恢复

SpringCloud Alibaba Sentinel实现熔断与限流

异常比例

异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒请求量 >= N(可配置),并且每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

SpringCloud Alibaba Sentinel实现熔断与限流

修改代码,重新启动

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

此时当我们访问的每秒QPS>=5 且异常比例大于配置的0.2(我们百分百出错)时服务降级。

SpringCloud Alibaba Sentinel实现熔断与限流

异常数

异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。

异常数是按照分钟统计的,时间窗口一定要大于等于60秒

SpringCloud Alibaba Sentinel实现熔断与限流

controller添加代码,重新启动访问

SpringCloud Alibaba Sentinel实现熔断与限流

@GetMapping("/testE")
public String testE(){
    log.info("testE 测试异常数");
    int age = 10/0;
    return "----testE 测试异常数";
}

SpringCloud Alibaba Sentinel实现熔断与限流

访问http://localhost:8401/testE

SpringCloud Alibaba Sentinel实现熔断与限流

1分钟内,前面5次访问都能访问testE,而testE百分之百报错,第五次时异常数已经大于5,第六次还是异常报错,异常数6>5,触发降级

热点参数限流

github.com/alibaba/Sen…

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

SpringCloud Alibaba Sentinel实现熔断与限流

Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。

兜底方法分为系统默认和客户自定义两种,之前的案例,限流出问题后,都是永sentinel系统默认的提示:Blocked by Sentinel (flow limiting)我们能不能自定义?类型hystrix,某个方法出问题了,就找对应的兜底降级方法?

结论:从@HystrixCommand到@SentinelResource

基本配置

controller中添加代码,重启

SpringCloud Alibaba Sentinel实现熔断与限流

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                         @RequestParam(value = "p2",required = false) String p2) {
    //int age = 10/0;
    return "------testHotKey";
}

//兜底方法
public String deal_testHotKey (String p1, String p2, BlockException exception){
    return "------deal_testHotKey,o(╥﹏╥)o";
}

使用@SentinelResource,value属性值可以随便取,保证唯一即可。blockHandler值为兜底方法名称。

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

当我们访问http://localhost:8401/testHotKey?p1=1

SpringCloud Alibaba Sentinel实现熔断与限流

当我们携带参数p1访问的时候,只有QPS超过了阈值设定的1就出现了限流,并走的是我们自定义的兜底方法。

注意如果@SentinelResource没有配置blockHandler,重新启动

SpringCloud Alibaba Sentinel实现熔断与限流

此时出现了限流,是直接显示异常,异常打到了前台用户界面看不到,不友好

SpringCloud Alibaba Sentinel实现熔断与限流

我们虽然对参数p1进行了限流但是只要不携带参数p1,或者p1参数QPS不超过阈值都可以正常访问。我们访问http://localhost:8401/testHotKey?p2=1

SpringCloud Alibaba Sentinel实现熔断与限流

怎么访问都没事,因为并没有对p2参数进行限流配置。

参数例外项

上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流。但是我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样。假如当p1的值等于5时,它的阈值可以达到200

热点参数的注意点,参数必须是基本类型或者String

对已有热点规则点击编辑

SpringCloud Alibaba Sentinel实现熔断与限流

点击高级选项,配置

SpringCloud Alibaba Sentinel实现熔断与限流

点击添加

SpringCloud Alibaba Sentinel实现熔断与限流

保存,例外项数目相应加1

SpringCloud Alibaba Sentinel实现熔断与限流

访问http://localhost:8401/testHotKey?p1=1,依旧按照配置限流

SpringCloud Alibaba Sentinel实现熔断与限流

访问http://localhost:8401/testHotKey?p1=5特列

SpringCloud Alibaba Sentinel实现熔断与限流

多次访问,都没有出现限流,说明配置生效!当p1等于5的时候,阈值变为200

其他测试

当我们在controller中的请求方法添加异常,重新启动

SpringCloud Alibaba Sentinel实现熔断与限流

访问http://localhost:8401/testHotKey

SpringCloud Alibaba Sentinel实现熔断与限流

并不会触发兜底方法

这是因为@SentinelResource处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理,@SentinelResource主管配置出错,运行出错该走异常走异常

系统自适应限流

github.com/alibaba/Sen…

在开始之前,我们先了解一下系统保护的目的:

  • 保证系统不被拖垮
  • 在系统稳定的前提下,保持系统的吞吐量

长期以来,系统保护的思路是根据硬指标,即系统的负载 (load1) 来做系统过载保护。当系统负载高于某个阈值,就禁止或者减少流量的进入;当 load 开始好转,则恢复流量的进入。这个思路给我们带来了不可避免的两个问题:

  • load 是一个“结果”,如果根据 load 的情况来调节流量的通过率,那么就始终有延迟性。也就意味着通过率的任何调整,都会过一段时间才能看到效果。当前通过率是使 load 恶化的一个动作,那么也至少要过 1 秒之后才能观测到;同理,如果当前通过率调整是让 load 好转的一个动作,也需要 1 秒之后才能继续调整,这样就浪费了系统的处理能力。所以我们看到的曲线,总是会有抖动。
  • 恢复慢。想象一下这样的一个场景(真实),出现了这样一个问题,下游应用不可靠,导致应用 RT 很高,从而 load 到了一个很高的点。过了一段时间之后下游应用恢复了,应用 RT 也相应减少。这个时候,其实应该大幅度增大流量的通过率;但是由于这个时候 load 仍然很高,通过率的恢复仍然不高。

TCP BBR 的思想给了我们一个很大的启发。我们应该根据系统能够处理的请求,和允许进来的请求,来做平衡,而不是根据一个间接的指标(系统 load)来做限流。最终我们追求的目标是 在系统不被拖垮的情况下,提高系统的吞吐率,而不是 load 一定要到低于某个阈值。如果我们还是按照固有的思维,超过特定的 load 就禁止流量进入,系统 load 恢复就放开流量,这样做的结果是无论我们怎么调参数,调比例,都是按照果来调节因,都无法取得良好的效果。

Sentinel 在系统自适应保护的做法是,用 load1 作为启动自适应保护的因子,而允许通过的流量由处理请求的能力,即请求的响应时间以及当前系统正在处理的请求速率来决定。

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

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

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

SpringCloud Alibaba Sentinel实现熔断与限流

  • 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 达到阈值即触发系统保护。

新增规则

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

访问没有任何配置的/testAhttp://localhost:8401/testA

SpringCloud Alibaba Sentinel实现熔断与限流

当QPS超过系统配置规则1的时候出现了限流!

@SentinelResource

注意:注解方式埋点不支持 private 方法。

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:

  • value:资源名称,必需项(不能为空)
  • entryType:entry 类型,可选项(默认为 EntryType.OUT
  • blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • fallback / fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandlerfallbackdefaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。

模块cloudalibaba-sentinel-service8401引入cloud-api-commons依赖,用于测试

<dependency>
    <groupId>com.kylin</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

创建RateLimitController

SpringCloud Alibaba Sentinel实现熔断与限流

@RestController
public class RateLimitController {
    @GetMapping("/byResource")
    @SentinelResource(value = "byResource", blockHandler = "handleException")
    public CommonResult byResource() {
        return new CommonResult(200, "按资源名称限流测试OK", new Payment(2020L, "serial001"));
    }

    public CommonResult handleException(BlockException exception) {
        return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用");
    }
}

启动8401

按资源名称限流

SpringCloud Alibaba Sentinel实现熔断与限流

访问http://localhost:8401/byResource

SpringCloud Alibaba Sentinel实现熔断与限流

1秒钟点击1下,OK。超过上述问题,疯狂点击,返回了自己定义的限流处理信息,限流发送

按照Url地址限流

通过访问的URL来限流,会返回Sentinel自带默认的限流处理信息

controller中添加代码,重新启动

SpringCloud Alibaba Sentinel实现熔断与限流

@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl() {
    return new CommonResult(200, "按url限流测试OK", new Payment(2020L, "serial002"));
}

SpringCloud Alibaba Sentinel实现熔断与限流

测试访问http://localhost:8401/rateLimit/byUrl

SpringCloud Alibaba Sentinel实现熔断与限流

1秒钟点击1下,OK。超过上述问题,疯狂点击,返回了Sentinel自带的的限流处理信息,限流发送

结论:按资源名称限流和URI地址限流效果配置效果都一样,但是按照URI地址配置限流是不能自定义限流处理信息的,也就是不能配置兜底方法,即使使用了blockHandler属性配置,也不生效

客户自定义限流处理逻辑

上面兜底方法面临的问题:

  • 系统默认的没有体现我们自己的业务要求。
  • 依照现有条件,我们自定义的处理方法又和业务代码耦合在一切,不直观。
  • 每个业务方法都添加一个兜底的,代码膨胀加剧。
  • 全局统一的处理方法没有体现

必须使用@SentinelResource资源名才能自定义限流处理逻辑

创建CustomerBlockHandler类用于自定义限流处理逻辑

SpringCloud Alibaba Sentinel实现熔断与限流

在这个类中创建两个方法,规定只能是static静态方法,方法参数类型是BlockException

public class CustomerBlockHandler {

    public static CommonResult handlerException(BlockException exception) {
        return new CommonResult(444, "按客户自定义,global,handlerException----1");
    }

    public static CommonResult handlerException2(BlockException exception) {
        return new CommonResult(444, "按客户自定义,global,handlerException----2");
    }
}

controller中添加代码

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "CustomerBlockHandler",
        blockHandlerClass = CustomerBlockHandler.class,
        blockHandler = "handlerException2")
public CommonResult customerBlockHandler() {
    return new CommonResult(200, "按客戶自定义", new Payment(2020L, "serial003"));
}

blockHanlderClass是自定义处理的类

此时的blockHanlder值是自定义处理类

中的哪一个方法

启动测试

SpringCloud Alibaba Sentinel实现熔断与限流

访问http://localhost:8401/rateLimit/customerBlockHandler

SpringCloud Alibaba Sentinel实现熔断与限流

一秒一次访问ok,超过QPS阈值1开始限流,显示我们自定义的限流处理逻辑,成功!

核心API

  • SphU定义资源
  • Tracer定义统计
  • ContextUtil定义了上下文

了解即可

服务熔断功能

Ribbon系列

环境搭建

提供者

新建cloudalibaba-provider-payment9003/9004

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

pom.xml

<dependencies>
    <!--SpringCloud ailibaba nacos -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <groupId>com.kylin</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!-- SpringBoot整合Web组件 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--日常通用jar包配置-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

yml,只要端口号不同

SpringCloud Alibaba Sentinel实现熔断与限流

server:
  port: 端口号

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

主启动类PaymentMain9003``PaymentMain9004

SpringCloud Alibaba Sentinel实现熔断与限流

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9003.class, args);
    }
}

新建controllerPaymentController

SpringCloud Alibaba Sentinel实现熔断与限流

@RestController
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    public static HashMap<Long, Payment> hashMap = new HashMap<>();
    static{
        hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
        hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
        hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
    }

    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        Payment payment = hashMap.get(id);
        CommonResult<Payment> result = new CommonResult(200,"from mysql,serverPort:  "+serverPort,payment);
        return result;
    }
    
}

启动访问测试http://localhost:9003/paymentSQL/1http://localhost:9004/paymentSQL/1

SpringCloud Alibaba Sentinel实现熔断与限流

搭建成功!

消费者

新建模块cloudalibaba-consumer-nacos-order84

SpringCloud Alibaba Sentinel实现熔断与限流

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <dependency>
        <groupId>com.kylin</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

application.yml

SpringCloud Alibaba Sentinel实现熔断与限流

server:
  port: 84


spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719

service-url:
  nacos-user-service: http://nacos-payment-provider

主启动类OrderNacosMain84

SpringCloud Alibaba Sentinel实现熔断与限流

@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain84
{
    public static void main(String[] args) {
        SpringApplication.run(OrderNacosMain84.class, args);
    }
}

RestTemplate配置类

SpringCloud Alibaba Sentinel实现熔断与限流

@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

创建controllerCircleBreakerController

SpringCloud Alibaba Sentinel实现熔断与限流

package com.kylin.config.controller;


import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.kylin.entities.CommonResult;
import com.kylin.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@Slf4j
public class CircleBreakerController {

    public static final String SERVICE_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;



    @RequestMapping("/consumer/fallback/{id}")
    //@SentinelResource(value = "fallback") //没有配置
    //@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
    //@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
    //@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
    //        exceptionsToIgnore = {IllegalArgumentException.class})
    public CommonResult<Payment> fallback(@PathVariable Long id) {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id, CommonResult.class,id);

        if (id == 4) {
            throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }

    //fallback
    public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
    }

    //blockHandler
    public CommonResult blockHandler(@PathVariable  Long id, BlockException blockException) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
    }
}

测试访问http://localhost:84/consumer/fallback/1

SpringCloud Alibaba Sentinel实现熔断与限流

访问成功,负载均衡成功!此时访问id为4的时候http://localhost:84/consumer/fallback/4出现异常

SpringCloud Alibaba Sentinel实现熔断与限流

id为5http://localhost:84/consumer/fallback/5

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

都是报的我们在controller对id进行判断而抛出的异常,环境搭建成功.但是都是给客户error页面,不友好

只配置fallback

此时访问http://localhost:84/consumer/fallback/4

SpringCloud Alibaba Sentinel实现熔断与限流

次数访问http://localhost:84/consumer/fallback/5

SpringCloud Alibaba Sentinel实现熔断与限流

fallback管运行异常

只配置blockHandler

SpringCloud Alibaba Sentinel实现熔断与限流

blockHandler只负责sentinel控制台配置违规

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

此时访问http://localhost:84/consumer/fallback/5

SpringCloud Alibaba Sentinel实现熔断与限流

访问两次出现异常,第三次访问出现异常,异常数大于阈值2触发服务降级,调用我们blockHandler中配置的方法

fallback和blockHandler都配置

重新启动

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

此时访问http://localhost:84/consumer/fallback/1

SpringCloud Alibaba Sentinel实现熔断与限流

QPS小于2时正常访问,QPS大于2时触发服务降级,调用blockHandler中配置的方法

此时访问http://localhost:84/consumer/fallback/5

SpringCloud Alibaba Sentinel实现熔断与限流

程序出现异常,调用fallback配置的方法处理。

如果以QPS大于2的情况访问会出现程序异常的http://localhost:84/consumer/fallback/4,触发服务降级,是调用fallback还是blockHandler呢?

SpringCloud Alibaba Sentinel实现熔断与限流

当业务异常sentinel控制台配置违规都发生时只会进入blockHandler处理逻辑

忽略属性

SpringCloud Alibaba Sentinel实现熔断与限流

通过exceptionsToIgnore忽略了id等于4抛出的IllegalArgumentException

重新启动此时访问http://localhost:84/consumer/fallback/4

SpringCloud Alibaba Sentinel实现熔断与限流

出现报错页面,而不是走fallback方法,因为被忽略了

Feign系列

修改84模块,pom引入openfeign依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

yml配置文件中开启对Feign的支持feign.sentinel.enabled=true

SpringCloud Alibaba Sentinel实现熔断与限流

#对Feign的支持
feign:
  sentinel:
    enabled: true

主启动类加上@EnableFeignClients启动Feign的功能

SpringCloud Alibaba Sentinel实现熔断与限流

@EnableDiscoveryClient
@SpringBootApplication
//启动Feign的功能
@EnableFeignClients
public class OrderNacosMain84
{
    public static void main(String[] args) {
        SpringApplication.run(OrderNacosMain84.class, args);
    }
}

创建PaymentService接口

SpringCloud Alibaba Sentinel实现熔断与限流

@FeignClient(value = "nacos-payment-provider", fallback = PaymentFallbackService.class)
public interface PaymentService {
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

创建PaymentFallbackService实现接口

SpringCloud Alibaba Sentinel实现熔断与限流

@Service
public class PaymentFallbackService implements PaymentService {
    @Override
    public CommonResult<Payment> paymentSQL(Long id) {
        return new CommonResult<>(44444, "服务降级返回,---PaymentFallbackService", new Payment(id, "errorSerial"));
    }
}

controller添加代码

SpringCloud Alibaba Sentinel实现熔断与限流

@Resource
PaymentService paymentService;

@GetMapping(value = "/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
    return paymentService.paymentSQL(id);
}

重新启动,访问http://localhost:84/consumer/paymentSQL/1

SpringCloud Alibaba Sentinel实现熔断与限流

测试84调用9003,9004,此时故意关闭微服务提供者,看84消费侧自动降级,不会被耗死

SpringCloud Alibaba Sentinel实现熔断与限流

配置成功。这个测试时针对Feign的独有特性的测试。具体服务熔断功能和上文中的Ribbon测试一致只是换了一下服务调用方式,都是使用@SentinelResourcefallback属性,blockHandler属性,和Sentinel控制台的配置

Sentinel持久化

为了多次测试,我们多次重启了应用,但是一旦我们重启应用,Sentinel规则将消失,生产环境需要将配置规则进行持久化。

将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上Sentinel上的流控规则持续有效。

修改cloudalibaba-sentinel-service8401的pom添加sentinel-datasource-nacos依赖

<!--sentinel-datasource-nacos持久化-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

修改配置文件添加Nacos数据源配置

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        #Nacos 服务注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        #配置Sentinel dashboard
        dashboard: localhost:8080
        #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

management:
  endpoints:
    web:
      exposure:
        include: '*'

添加Nacos业务规则配置,访问`http:SpringCloud Alibaba Sentinel实现熔断与限流

图片中的reta因为rate 配置内容

[
    {
         "resource": "/rateLimit/byUrl",
         "limitApp": "default",
         "grade":   1,
         "count":   1,
         "strategy": 0,
         "controlBehavior": 0,
         "clusterMode": false    
    }
]
  • resource:资源名称
  • limitApp:来源应用
  • grade:阈值类型,0表示线程数,1表示QPS
  • count:单机阈值
  • strategy:流控模式,0表示直接,1表示关联,2表示链路
  • controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待

重新启动8401,查看

SpringCloud Alibaba Sentinel实现熔断与限流

此时已经自动的把我们配置内容转换成相应的流控规则了,访问测试http://localhost:8401/rateLimit/byUrl

SpringCloud Alibaba Sentinel实现熔断与限流

限流规则也生效。每次重启应用后都会自动按照配置在Nacos中的配置文件自动配置相应的流控规则。(感觉没想象中的好用这个功能)