欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

程序员文章站 2022-07-15 09:23:02
...

文章目录

Sentinel概述


1、官网

https://github.com/alibaba/Sentinel

中文:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

2、Sentinel是什么?

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等
  • 完备的实时监控:Sentinel 同时提供实时的监功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel
  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等

与Hystrix对比

Hystrix Sentinel
需要程序员自己手工搭建监控平台 单独一个组件,可以独立出来
没有一套web界面可以给我们进行更加细粒度化的配置 直接界面的细粒度统一配置

3、Sentinel去哪下?

https://github.com/alibaba/Sentinel/releases

3、Sentinel能干嘛?

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

4、Sentinel怎么玩?

官方使用文档:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel

解决微服务中的各种问题

  • 服务雪崩
  • 服务降级
  • 服务熔断
  • 服务限流

安装Sentinel控制台


1、Sentinel两部分组件

核心库

Java客户端,不依赖于任何框架/库,能够运行于所有Java运行时环境,同时对Dubbo/Spring Cloud等框架也有较好的支持

控制台

Danshboard,基于Spring Boot开发,打包后可以直接运行。不需要额外的Tomcat等应用容器

2、安装步骤

下载

官方下载地址:https://github.com/alibaba/Sentinel/releases

下载到本地:sentinel-dashboard-1.7.0.jar

运行命令

前提是Java8+环境OK与8080端口不能被占用

命令:java -jar sentinel-dashboard-1.7.0.jar

访问sentinel管理页面

地址:http://localhost:8080

登录账号密码均为:sentinel

初始化演示工程


1、Nacos启动成功

http://localhost:8848/nacos

2、工程

Module

新建cloudalibaba-sentinel-service8401工程

Pom

    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
		<!--后续做数据持久化会用到-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</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>
    </dependencies>

Yaml

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        #配置 Sentinel dashboard 地址
        dashboard: localhost:8080
        #sentinel会启动一个http server与dashboard进行通信,占用该端口
        port: 8719  #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口

主启动

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

业务类FlowLimitController

@RestController
public class FlowLimitController {

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

    @GetMapping("/testB")
    public String testB() {
        return "------testB";
    }
}

3、测试

启动Sentinel8080 —> java -jar sentinel-dashboard-1.7.0.jar

启动微服务8401

启动微服务8401后查看sentinel控制台,发现空空如也,啥都没有

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

这是因为sentinel采用了懒加载机制,只要让它检测到就会有对应监控服务

http://localhost:8401/testA + http://localhost:8401/testB

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

出现了上述结果就说明sentinel8080正在监控微服务8401!

流控规则


1、基本介绍

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

上图概念解释说明

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

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

  • 阈值类型/单机阈值:

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

  • 流控模式:

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

    • 快速失败:直接失败,抛异常
    • Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值
    • 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效

2、流控模式

2.1、直接(默认)

直接 —> 快速失败

配置及说明

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

注:若阈值类型为线程数的话,表示只允许一个线程处理该请求(干活的只允许有一个)

测试

快速点击访问http://localhost:8401/testA(超过一秒一次)

结果:Blocked by Sentinel (flow limiting)

2.2、关联

关联是当关联的资源达到阈值后,就限流自己,当与A相关联的B达到阈值后,就限流A,即B惹事,A挂了

配置及说明

当关联资源的/testB的QPS阈值超过1时,就限流/testA的Rest访问地址

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

测试

用Postman模拟并发密集访问testB

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

运行后发现访问testA挂了,—> 访问http://localhost:8401/testA 结果:Blocked by Sentinel (flow limiting)

2.3、链路

链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流;它的功能有点类似于针对来源配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度更细

3、流控效果

3.1、快速失败

这是默认的流控处理,直接失败,抛出异常 Blocked by Sentinel (flow limiting)

规则源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

3.2、预热(Warm Up)

官网

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

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

即请求QPS从阈值/3开始,经过预热时长逐渐升至指定的QPS阈值

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

表明一开始的单机阈值为10/3为3,然后预热时长为3,表明经过3秒时长后会逐渐升至指定的QPS阈值

规则源码:com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

场景

秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放迸慢慢的把阀值增长到设置的阀值

3.3、排队等待

官网

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

配置及说明

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

/testA每秒处理一次请求,超过的话就排队等待,等待的超时时间为20000毫秒

配置源码:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController

测试

开启Postman测试,每隔0.1秒发送一个请求

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

发现只有第一次立即响应了,其他几次都是接近一秒后进行响应,测试成功!

降级规则


1、概述

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。

2、熔断策略

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

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 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断

注意:针对 Sentinel 1.8.0 及以上版本

3、熔断降级规则说明

源码规则类:com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule

熔断降级规则(DegradeRule)包含下面几个重要的属性:

Field 说明 默认值
resource 资源名,即规则的作用对象
grade 熔断策略,支持慢调用比例/异常比例/异常数策略 慢调用比例
count 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow 熔断时长,单位为 s
minRequestAmount 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) 5
statIntervalMs 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) 1000 ms
slowRatioThreshold 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)

热点key限流


1、基本介绍

1.1、热点key限流是什么?

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

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

1.2、官网

https://github.com/alibaba/Sentinel/wiki/热点参数限流

1.3、源码逻辑实现

com.alibaba.csp.sentinel.slots.block.BlockException

2、代码测试

2.1、编写方法

    @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey",blockHandler = "dealTestHotKey")	//value随便写,只要不重复
    public String testHotKey(String p1,String p2) {
        return "testHotKey";
    }
	//兜底方法
    public String dealTestHotKey(String p1, String p2, BlockException exception) {
        return "deal test hot key ! ";
    }

2.2、配置

sentinel控制台新增热点规则

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

这样方法testHotKey中的第一个参数只要QPS超过每秒一次,马上降级处理,若@SentinelResource注解没配置blockHandler属性的话,异常就会打到前台被用户看到,若配置了则使用自定义的兜底方法

2.3、测试

http://localhost:8401/testHotKey?p1=a -> QPS超过阈值则失败!

http://localhost:8401/testHotKey?p1=a&p2=b -> QPS超过阈值则失败!

http://localhost:8401/testHotKey?p2=b -> 成功!

3、高级选项

3.1、特殊情况

上述案例演示了第一个参数p1当QPS超过一以后马上被限流

我们期望p1参数当它是一个特殊的值的时候,它的限流值和平时不一样

比如当p1的值等于5时,它的阈值可以达到200

3.2、配置

注意:参数的类型必须为8中基本数据类型或String

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

3.3、测试

http://localhost:8401/testHotKey?p1=5 -> 成功!

http://localhost:8401/testHotKey?p1=3 -> 失败,QPS>1会被限流

当p1=5的时候,阈值会变为200,当p1不等于5的时候。阈值就是平常的1

系统规则


1、概述

Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性

2、各项配置参数说明

系统保护规则是从应用级别的入口流量进行控制,从单台机器的 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 达到阈值即触发系统保护

3、配置界面

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

@SentinelResource


1、按照资源名称限流

1.1、测试方法

注:CommonResult为自己封装的返回信息的泛型类,返回状态码,信息及相应数据

SpringCloudAlibaba笔记——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 服务不可用");
    }
}

1.2、配置流控规则

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

结合我们的代码注解,表明一秒钟内查询次数大于1就跑到我们自定义的处理限流的方法,

1.3、测试

一秒钟点击一下,ok!

超过上述问题,疯狂点击,返回了自定义的限流处理信息

{"code":444,"message":"com.alibaba.csp.sentinel.slots.block.flow.FlowException\t 服务不可用","data":null}

2、按照URL地址限流

参考之前的流控规则的基本介绍

3、上面兜底方法面临的问题

  • 系统默认的,没有体现我们自己的业务要求。

  • 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观

  • 每个业务方法都添加一个兜底的,那代码膨胀加剧

  • 全局统—的处理方法没有体现

4、客户自定义限流处理逻辑

4.1、自定义限流处理类

public class CustomerBlockHandler {

    public static CommonResult handlerException(BlockException exception) {
        return new CommonResult(4444,"按客戶自定义  global handler exception1",new Payment(2020L,"serial003"));
    }

    public static CommonResult handlerException2(BlockException exception) {
        return new CommonResult(4444,"按客戶自定义  global handler exception2",new Payment(2020L,"serial003"));
    }
}

4.2、测试方法

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

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

4.3、流控配置

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

4.4、测试

一秒钟点击一下,ok!

超过上述问题,疯狂点击,返回了自定义的限流处理信息

{"code":4444,"message":"按客戶自定义  global handler exception2","data":{"id":2020,"serial":"serial003"}}

可以看到是CustomerBlockHandler的handlerException2方法执行了,配置成功!

5、注解属性说明

value

资源名称,必需项,因为需要通过resource name找到对应的规则,这个是必须配置的。

entryType

entry 类型,可选项,
有IN和OUT两个选项,默认为 EntryType.OUT。

public enum EntryType {
    IN("IN"),
    OUT("OUT");
}

blockHandler

blockHandler 对应处理 BlockException 的函数名称,可选项。
blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,
参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。

blockHandlerClass

blockHandler 函数默认需要和原方法在同一个类中,如果希望使用其他类的函数,
则需要指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

fallback

fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。
fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。

fallbackClass

fallbackClass的应用和blockHandlerClass类似,fallback 函数默认需要和原方法在同一个类中。
若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

defaultFallback(since 1.6.0)

如果没有配置defaultFallback方法,默认都会走到这里来。
默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑。
默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。
若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。

exceptionsToIgnore(since 1.6.0)

用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

服务熔断功能


sentinel整合 ribbon + openfeign + fallback

1、Ribbon系列

1.1、服务提供者9003/9004

新建cloudalibaba-provider-payment9003/9004

POM

<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</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>
</dependencies>

YAML

server:
  port: 9003

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

主启动

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

业务类

@RestController
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    public static HashMap<Long, Payment> hashMap = new HashMap<Long, Payment>();

    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);
        return new CommonResult<Payment>(200,"from mysql,serverPort:  " + serverPort, payment);
    }
}

测试地址

http://localhost:9003/paymentSQL/1

1.2、消费者84

新建cloudalibaba-consumer-nacos-order84

POM

<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</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>
</dependencies>

YAML

server:
  port: 84

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

主启动

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

业务类

ApplicationContextConfig:用来给容器中填一个带负载均衡的RestTemplate组件

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

CircleBreakerController

@RestController
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<Payment> handlerFallback(@PathVariable  Long id,Throwable e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<Payment>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
    }

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

1.3、测试

1.3.1、没有任何配置

@SentinelResource(value = “fallback”)

http://localhost:84/consumer/fallback/1 -> 返回正常

http://localhost:84/consumer/fallback/4 -> 报错!非法参数异常

http://localhost:84/consumer/fallback/8 -> 报错!空指针异常

1.3.2、只配置fallback

@SentinelResource(value = “fallback”,fallback = “handlerFallback”)

http://localhost:84/consumer/fallback/1 -> 返回正常

http://localhost:84/consumer/fallback/4 -> 返回指定内容

{"code":444,"message":"兜底异常handlerFallback,exception内容  IllegalArgumentException,非法参数异常....","data":{"id":4,"serial":"null"}}

http://localhost:84/consumer/fallback/8 -> 返回指定内容

{"code":444,"message":"兜底异常handlerFallback,exception内容  NullPointerException,该ID没有对应记录,空指针异常","data":{"id":5,"serial":"null"}}
1.3.3、只配置blockhandler

@SentinelResource(value = “fallback”,blockHandler = “blockHandler”)

sentinel控制台配置对应流控规则

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

http://localhost:84/consumer/fallback/1 -> 返回正常

http://localhost:84/consumer/fallback/4 -> QPS<1报错,大于1时候返回指定内容

{"code":445,"message":"blockHandler-sentinel限流,无此流水: blockException null","data":{"id":4,"serial":"null"}}

http://localhost:84/consumer/fallback/8 -> QPS<1报错,大于1时候返回指定内容

{"code":445,"message":"blockHandler-sentinel限流,无此流水: blockException null","data":{"id":8,"serial":"null"}}
1.3.3、blockhandler与fallback都配置

@SentinelResource(value = “fallback”,fallback = “handlerFallback”,blockHandler = “blockHandler”)

sentinel控制台配置对应流控规则

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

当QPS<1时:

http://localhost:84/consumer/fallback/1 -> 返回正常

http://localhost:84/consumer/fallback/4 -> 返回指定内容

{"code":444,"message":"兜底异常handlerFallback,exception内容  IllegalArgumentException,非法参数异常....","data":{"id":4,"serial":"null"}}

http://localhost:84/consumer/fallback/8 -> 返回指定内容

{"code":444,"message":"兜底异常handlerFallback,exception内容  NullPointerException,该ID没有对应记录,空指针异常","data":{"id":8,"serial":"null"}}

当QPS>1时:

http://localhost:84/consumer/fallback/1 -> 返回指定内容

{"code":445,"message":"blockHandler-sentinel限流,无此流水: blockException null","data":{"id":1,"serial":"null"}}

http://localhost:84/consumer/fallback/4 -> 返回指定内容

{"code":445,"message":"blockHandler-sentinel限流,无此流水: blockException null","data":{"id":4,"serial":"null"}}

http://localhost:84/consumer/fallback/8 -> 返回指定内容

{"code":445,"message":"blockHandler-sentinel限流,无此流水: blockException null","data":{"id":8,"serial":"null"}}

若blockHandler和fallback都进行了配置,则被限流降级而抛出BlockException时只会进入blockHandler处理逻辑

1.3.4、exceptionsToIgnore属性

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

2、Feign系列

2.1、修改84模块

POM

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

YAML

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

业务类

带@FeignClient注解的业务接口

@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {

    @GetMapping("/paymentSQL/{id}")
    CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

实现类PaymentFallbackService

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

controller

// OpenFeign
@Resource
private PaymentService paymentService;

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

主启动类

添加@EnableFeignClients启动Feign的功能

2.2、测试

http://localhost:84/paymentSQL/1

成功返回数据

{"code":200,"message":"from mysql,serverPort:  9003","data":{"id":1,"serial":"28a8c1e3bc2742d8848569891fb42181"}}

关闭服务提供者,看是否会进行超时服务降级

http://localhost:84/paymentSQL/1

{"code":44444,"message":"服务降级返回,---PaymentFallbackService","data":{"id":1,"serial":"errorSerial"}}

测试成功!

3、熔断框架比较

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

Sentinel规则持久化


1、是什么?

一旦我们重启应用,Sentinel规则将消失,生产环境需要将配置规则进行持久化

2、怎么玩?

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

3、步骤

3.1、修改模块代码

修改cloudalibaba-sentinel-service8401

POM

添加做配置持久化所需要的依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

YAML

添加Nacos数据源配置 格式如下

spring:
   cloud:
    sentinel:
    datasource:
     ds1:
      nacos:
        server-addr: localhost:8848
        dataid: ${spring.application.name}
        groupid: DEFAULT_GROUP
        data-type: json
         rule-type: flow

修改后yaml

server:
  port: 8401

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

3.2、添加Nacos业务规则配置

SpringCloudAlibaba笔记——Sentinel实现熔断与限流

内容解释

resource:资源名称

limitApp:来源应用

grade:阈值类型,0表示线程数,1表示QPS

count:单机阈值

strategy:流控模式,0表示直接,1表示关联,2表示链路

controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待

clusterMode:是否集群

3.3、测试

重启后测试http://localhost:8401//rateLimit/byUrl

发现当QPS大于1时候流控规则起效果了,并且Sentinel控制台也显示出来了,说明持久化配置成功!