Spring Cloud Alibaba - Sentinel(二)

目录

一、Sentinel 熔断降级简介

1、基本介绍

2、熔断策略

3、熔断规则

二、Sentinel熔断策略

1、慢调用比例

2、异常比例

3、 异常数

三、热点规则

1、热点规则

2、参数例外项

四、系统规则

1、Sentinel 系统规则

五、自定义限流逻辑处理

1、@SentinelResource 自定义限流逻辑处理

1.1、@SentinelResource 资源限流

1.2、自定义限流处理逻辑

2、Sentinel整合Ribbon+openFeign

2.1、环境搭建

3、SentinelResource的fallback属性

4、exceptionsToIgnore属性


一、Sentinel 熔断降级简介

1、基本介绍

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

Spring Cloud Alibaba - Sentinel(二)_第1张图片

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

2、熔断策略

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 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常。

Sentinel在1.8.0版本对熔断降级做了大的调整,可以定义任意时长的熔断时间,引入了半开启恢复支持。
熔断状态有三种状态,分别为OPEN、HALF_OPEN、CLOSED

状态 说明

OPEN

表示熔断开启,拒绝所有请求

HALF_OPEN

探测恢复状态,如果接下来的一个请求顺利通过则表示结束熔断,否则继续熔断

CLOSE

表示熔断关闭,请求顺利通过

3、熔断规则

熔断降级规则(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 引入)

Sentinel官方文档

二、Sentinel熔断策略

1、慢调用比例

概念:选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(`statIntervalMs`)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。

Spring Cloud Alibaba - Sentinel(二)_第2张图片

Spring Cloud Alibaba - Sentinel(二)_第3张图片

 具体演示

首先我们先添加一个控制器方法:

//FlowLimitController.java
@GetMapping("/testC")
public String testC(){
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "----testC";
}

设置熔断策略,1QPS>5 并且这些请求的RT>300 并且大于比例阈值触发熔断

Spring Cloud Alibaba - Sentinel(二)_第4张图片

http://localhost:8401/testC

测试

通过JMeter测试,1秒钟发起10个线程请求/testC,此时就会触发熔断效果,停止测试以后,10秒钟以后恢复正常

Spring Cloud Alibaba - Sentinel(二)_第5张图片

Spring Cloud Alibaba - Sentinel(二)_第6张图片

 启动--线程组

Spring Cloud Alibaba - Sentinel(二)_第7张图片

 停止JMeter,10秒后再访问testC接口,会成功

Spring Cloud Alibaba - Sentinel(二)_第8张图片

2、异常比例

        概念:异常比例 (`ERROR_RATIO`):当单位统计时长(`statIntervalMs`)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 `[0.0, 1.0]`,代表 0% - 100%。
注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(`BlockException`)不生效。

Spring Cloud Alibaba - Sentinel(二)_第9张图片

具体演示

//FlowLimitController
@GetMapping("/testD")
public String testD(Integer id){
    if(id != null && id > 1){
        throw new RuntimeException("异常比例测试");
    }
    return "------------testD";
}

Spring Cloud Alibaba - Sentinel(二)_第10张图片

Spring Cloud Alibaba - Sentinel(二)_第11张图片

测试

我们通过JMeter来测试,设定HTTP请求地址

Spring Cloud Alibaba - Sentinel(二)_第12张图片

 Spring Cloud Alibaba - Sentinel(二)_第13张图片

当启动JMeter的时候,就会触发熔断,因为此时我们1秒钟发送10个请求超过了最小请求数5,同时超过了阈值,满足了两个条件,当熔断时长过后就会恢复正常。

Spring Cloud Alibaba - Sentinel(二)_第14张图片

 JMeter停止。

3、 异常数

        概念:异常数 (`ERROR_COUNT`):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(`BlockException`)不生效。

Spring Cloud Alibaba - Sentinel(二)_第15张图片

 具体演示

//FlowLimitController
@GetMapping("/testE")
public String testE(Integer id){
    if(id != null && id > 1){
        throw new RuntimeException("异常数测试");
    }
    return "------------testE";
}

设置异常数策略,当1秒钟内请求超过5并且异常数大约5个的时候触发熔断

Spring Cloud Alibaba - Sentinel(二)_第16张图片

测试

通过JMeter来测试

Spring Cloud Alibaba - Sentinel(二)_第17张图片

 1秒钟发送10个请求

Spring Cloud Alibaba - Sentinel(二)_第18张图片

 此时就会触发熔断

Spring Cloud Alibaba - Sentinel(二)_第19张图片

三、热点规则

1、热点规则

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

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

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。

sentine官网文档

Spring Cloud Alibaba - Sentinel(二)_第20张图片

 使用@SentinelResource注解

其实这个热点限流其实就是更加细粒度的流控规则,那么如果想使用它就必须要配合对应SentinelResource注解。
Sentinel 提供了 @SentinelResource 注解用于定义资源,它有很多的参数,我们这里主要关注两个参数:

  1.  value:代表资源名称,必需项,因为需要通过resource name找到对应的规则,这个是必须配置的
  2. blockHandler:blockHandler 对应处理 BlockException 的方法名称,可选项,访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。

具体演示

Spring Cloud Alibaba - Sentinel(二)_第21张图片

 @SentinelResource(value="xxx")

那现在我们要完成以上图中的效果,这个时候我们首先要编写代码,在FlowLimitController中编写代码

@GetMapping("/testHotKey")
@SentinelResource("testHotKey")
public String testHotKey(@RequestParam(value = "hot1",required = false) String hot1,
                         @RequestParam(value = "hot2",required = false) String hot2,
                         @RequestParam(value = "hot3",required = false) String hot3){
    return "----testHotKey";
}
http://localhost:8401/testHotKey?hot1=1

然后再来配置热点规则

Spring Cloud Alibaba - Sentinel(二)_第22张图片

 测试

此时如果我们传入参数hot1,并且超过阈值,就会出现限流,但是此时的限流效果为报错,显示不友好.

Spring Cloud Alibaba - Sentinel(二)_第23张图片

 @SentinelResource(value="xxx",blockHandler="xxx")

刚才的演示中,我们明显发现这种限流方法的提示效果非常不友好,所以如果我们需要能够得到友好的提示,我们就需要使用@SentinelResource注解提供的另外一个参数blockHandler,这个参数是可以指定当出现异常时的处理方法,具体操作如下:

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "handler_HotKey")
public String testHotKey(@RequestParam(value = "hot1",required = false) String hot1,
                         @RequestParam(value = "hot2",required = false) String hot2,
                         @RequestParam(value = "hot3",required = false) String hot3){
    return "----testHotKey";
}

//处理异常方法,方法签名要和对应的接口方法保持一致
public String handler_HotKey(String hot1, String hot2,String hot3,BlockException exception){
    return "系统繁忙稍后重试。。";
}

然后热点规则不变,我们最终的到的限流效果如下:

2、参数例外

        其实参数例外项就是可以达到更加细粒度的控制,比如我们当前的例子中,目前hot1参数在访问时超过阈值就会被限流,但是我们可以通过参数例外项设置hot1具体等于特殊的某个值的时候,触发不同的限流效果。假如hot1的值等于5时,它的阈值可以达到200
注意:参数例外项中的参数类型仅支持一下7种数据类型

Spring Cloud Alibaba - Sentinel(二)_第24张图片

 具体演示

当前我们需要让hot1的值为5的时候阈值可以达到200,首先Sentinel页面中修改对应热点规则

此时的规则为:如果当前hot1值为除5以外的其他值,都会走普通的阈值规则,但是如果一旦hot1的值为5的时候,将会走参数例外项,此时的阈值为200,我们通过浏览器测试,当hot1的值等于5是只要阈值不超过200就不会出现限流。

Spring Cloud Alibaba - Sentinel(二)_第25张图片

 添加-->保存。快频次访问 http://localhost:8401/testHotKey?hot1=5   达不到200阈值不限流。

注意:代码中使用了@SentinelResource注解,此注解处理的是Sentinel控制台配置的异常,通过blockHandler属性设置对应方法来处理和程序本身异常无关。

所以以下程序中如果hot1的值等于6还是会出现RuntimeException。

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "handler_HotKey")
public String testHotKey(@RequestParam(value = "hot1",required = false) String hot1,
                         @RequestParam(value = "hot2",required = false) String hot2,
                         @RequestParam(value = "hot3",required = false) String hot3){
    if("6".equals(hot1)){
        throw new RuntimeException("运行时异常");
    }
    return "-----testHotKey";
}

Spring Cloud Alibaba - Sentinel(二)_第26张图片

四、系统规则

1、Sentinel 系统规则

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

系统规则

        系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
        系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量,比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

 Spring Cloud Alibaba - Sentinel(二)_第27张图片

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

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

具体演示

这里我们只通过入口QPS来进行测试,直接设置规则

Spring Cloud Alibaba - Sentinel(二)_第28张图片

 最后测试效果不管现在我们访问那个接口只要超过阈值就会被限流

 

五、自定义限流逻辑处理

1、@SentinelResource 自定义限流逻辑处理

Sentinel 提供了@SentinelResource注解用于定义资源,并提供了AspectJ的扩展用于自定义资源,处理BlockException等。

@SentinelResource注解两个属性:

  • value:资源名称,必须项(唯一,不能为空)
  • blockHandler:对应处理BlockException的函数名称可选项.blockHandler函数访问需要public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为BlockException.blockHandler 函数默认需要和原方法在同一个类中

上面的热点规则进行限流以后,通过blockHandler属性设置限流以后的解决方法。
其实这个注解不仅仅可以用到热点规则上,还可以用到流控上,我们可以做一个资源的流控和一个请求的流控,通过此注解来解决限流之后问题。

1.1、@SentinelResource 资源限流

核心点:使用@SentinelResource注解的blockHandler属性,定义出现限流效果时的解决方法。

@RestController
public class SentinelResourceController {

    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handler_resource")
    public String byResource(){
        return "-----byResource";
    }

    public String handler_resource(BlockException exception){
        return "系统繁忙";
    }
}
http://localhost:8401/byResource

这里要注意一定要给byResource资源添加流控

Spring Cloud Alibaba - Sentinel(二)_第29张图片

 测试,测试我们去快速访问http://localhost:8401/byResource,就会出现我们使用@SentinelResource注解中blockHandler属性提供的解决限流异常的方法。

结论:

1. @SentinelResource 既可以配置资源名称也可以配置URL
2. 如果配置了@SentinelResource的blockHandler属性对应方法,出现限流会调用对应方法
3. 如果没有配置@SentinelResource的blockHandler属性,系统会走默认的限流处理。

1.2、自定义限流处理逻辑

其实我们在使用@SentinelResource注解这两种方案的时候,会出现一些问题:
1. 没有体现我们自己的业务要求。
2. 自定义处理方法和业务代码耦合在一起。
3. 每个业务方法都添加一个限流处理方法,代码将会加剧膨胀。
4. 无法实现统一全局处理。 
解决:

@SentinelResource除了blockHandler可以设置自定义限流处理逻辑方法以外,还提供另外一个属性来设置限流处理逻辑类型blockHandlerClass属性,此属性中设置的方法必需为 static 函数,否则无法解析。

具体逻辑

1、创建CustomerBlockHandler类型用于处理自定义限流处理逻辑,首先创建CustomerBlockHandler

/**
 * 此类型用来处理限流自定义逻辑
 */
public class CustomerBlockHandler {
    public static String handlerException1(BlockException exception){
        return "handlerException1:系统异常,请稍后重试!";
    }
    public static String handlerException2(BlockException exception){
        return "handlerException2:网络崩溃了,请稍后重试!";
    }
}

2、我们在SentinelResourceController类型中添加一个接口方法,同时设置@SentinelResource注解和blockHandlerClass属性对应的类型和这个类型中对应的处理方法

/**
* 此方法用到了自定义限流处理类型CustomerBlockHandler
* 中的handlerException1方法来处理限流逻辑。
*/
@GetMapping("/bycustomer")
@SentinelResource(value = "bycustomer",
                  blockHandlerClass = CustomerBlockHandler.class,
                  blockHandler = "handlerException1")
public String bycustomer(){
    return "-----bycustomer";
}

3、测试:给bycustomer资源添加限流规则,然后来测试在超过限流阈值时处理方法是否为CustomerBlockHandler中handlerException1来进行处理。

Spring Cloud Alibaba - Sentinel(二)_第30张图片

 添加流控规则以后,我们再来频繁访问http://localhost:8401/bycustomer,就会看见是CustomerBlockHandler类型的handlerException1方法来处理自定义限流逻辑

2、Sentinel整合Ribbon+openFeign

服务熔断:应对微服务雪崩效应的一种链路保护机制,类似保险丝。

2.1、环境搭建

需要利用Ribbon进行负载均衡的调用,所以我们需要创建一个服务消费者cloudalibaba-consumer8084和两个服务提供者cloudalibaba-provider9003和cloudalibaba-provider9004

在建立9003和9004之前,先建立一个共享项目cloud-api-commons,在其中新建一个类型JsonResult,这个类型用于返回JSON数据类型

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class JsonResult {
    private Integer code;
    private T data;
}

创建服务提供者9003,9004基本上是一样的

pom



    
        cloud2021
        com.lwz.springcloud
        1.0-SNAPSHOT
    
    4.0.0

    cloudalibaba-provider9003
    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
            org.springframework.boot
            spring-boot-devtools
            runtime
            true
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-sentinel
        
        
            com.lwz.springcloud
            cloud-api-commons
            ${project.version}
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

yml

server:
  port: 9003

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

主启动添加注解@EnableDiscoveryClient

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class CloudalibabaProvider9003Application {

    public static void main(String[] args) {
        SpringApplication.run(CloudalibabaProvider9003Application.class, args);
    }

}

控制器

import com.lwz.springcloud.entities.JsonResult;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

@RestController
public class DataController {

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

    //模仿数据库存储数据
    public static HashMap hashMap = new HashMap<>();
    static {
        hashMap.put(1l,"鼠标");
        hashMap.put(2l,"键盘");
        hashMap.put(3l,"耳机");
    }

    @GetMapping("info/{id}")
    public JsonResult getSql(@PathVariable("id") Long id){
        JsonResult result = new JsonResult(200,hashMap.get(id)+serverPort);
        return result;
    }
}

注意:9004和9003 一致,但是要注意修改yml文件端口号

新建cloudalibaba-consumer8084



    
        cloud2021
        com.lwz.springcloud
        1.0-SNAPSHOT
    
    4.0.0

    cloudalibaba-consumer8084
    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
            org.springframework.boot
            spring-boot-devtools
            runtime
            true
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-sentinel
        
        
            com.lwz.springcloud
            cloud-api-commons
            ${project.version}
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    


yml

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

#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-provider

主启动添加注解和9003/9004一致

config

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

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

}

最终测试

访问http://localhost:8084/consumer/fallback/2

查看最后结果是否为9003/9004切换调用

 

3、SentinelResource的fallback属性

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

  • 返回值类型必须与原函数返回值类型一致;
  • 方法参数列表需要和原函数一致,或者可以额外多一个 `Throwable` 类型的参数用于接收对应的异常。

fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 `fallbackClass` 为对应的类的 `Class` 对象,注意对应的函数必需为 static 函数,否则无法解析。
其实通过官网上提供的概念,我们不难看出这个属性类似于blockHandler,但是各位一定要注意他们有本质的不同。
注意:fallback属性和blockHandler属性的本质不同在于他们作用的异常不同:
blockHandler:针对违反Sentinel控制台配置规则时触发BlockException异常时对应处理的属性
fallback:针对Java本身出现的异常进行处理的对应属性。

具体演示

在8084项目的DemoController中编写对应代码

首先我们先来设置异常规则

@RestController
@Slf4j
public class DemoController {
    //服务提供者URL
    @Value("${service-url.nacos-user-service}")
    private String SERVICE_URL;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/consumer/fallback/{id}")
    public JsonResult fallback(@PathVariable Long id){
        if(id<=3){
            //通过Ribbon发起远程访问,访问9003/9004
            JsonResult result = restTemplate.getForObject(SERVICE_URL+"/info/"+id,JsonResult.class);
            System.err.println(result.getData());
            return result;
        }else{
            throw new NullPointerException("没有对应的数据记录");
        }
    }
}

此时我们任务添加了异常,此时如果我们访问http://localhost:8084/consumer/fallback/4(id非法)地址时,就会出现对应的显示效果:

Spring Cloud Alibaba - Sentinel(二)_第31张图片

 明显此时显示效果非常不好,我们就可以通过@SentinelResource注解的fallback属性来解决这种java异常,给出友好提示

@RestController
@Slf4j
public class DemoController {
    //服务提供者URL
    @Value("${service-url.nacos-user-service}")
    private String SERVICE_URL;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/consumer/fallback/{id}")
    //添加SentinelResource注解的fallback属性,同时设置方法来解决Java异常
    @SentinelResource(value = "falllback",fallback = "fallbackHandler")
    public JsonResult fallback(@PathVariable Long id){
        if(id<=3){
            //通过Ribbon发起远程访问,访问9003/9004
            JsonResult result = restTemplate.getForObject(SERVICE_URL+"/info/"+id,JsonResult.class);
            System.err.println(result.getData());
            return result;
        }else{
            throw new NullPointerException("没有对应的数据记录");
        }
    }
    //保证方法签名基本保持一致,但是要添加异常类型参数
    public JsonResult fallbackHandler(Long id,Throwable e){
        JsonResult result = new JsonResult<>(444,"出现未知商品id");
        return result;
    }
}

 到这里为止,我们就很清楚的知道了fallback属性的作用,同时它和blockHandler属性类似,也可以设置fallbackClass属性,来指定对应类型,来处理对应的Java异常,当然要注意和blockHandlerClass属性一样,也需要让所有的方法都必需为 static 函数,否则无法解析。

同时配置blockHandler和fallback属性

如果我们在使用@SentinelResource属性的时候,同时设置blockHandler属性和fallback属性时,并且同时出现了Sentinel异常和Java异常,这个时候会执行哪个方法那。

具体演示

同时在DemoController中设置fallback属性和blockHandler属性

Spring Cloud Alibaba - Sentinel(二)_第32张图片

 规则:在一秒内超过最小访问次数5次,并且异常数超过2的时候,就会触发熔断规则。

此时我们来访问http://localhost:8084/consumer/fallback/4看效果:

- 在没有触发熔断之前的异常交给fallback来处理

 - 但是一旦触发熔断规则就变成了blockHandler来处理

4、exceptionsToIgnore属性

- `exceptionsToIgnore`(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

@RestController
@Slf4j
public class DemoController {
    //服务提供者URL
    @Value("${service-url.nacos-user-service}")
    private String SERVICE_URL;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/consumer/fallback/{id}")
    //同时添加SentinelResource注解的fallback和blockHandler属性
    @SentinelResource(value = "falllback",fallback = "fallbackHandler",blockHandler = "blockHandler",
            exceptionsToIgnore = {NullPointerException.class})//被标注的异常将会被 原样抛出
    public JsonResult fallback(@PathVariable Long id){
        if(id<=3){
            //通过Ribbon发起远程访问,访问9003/9004
            JsonResult result = restTemplate.getForObject(SERVICE_URL+"/info/"+id,JsonResult.class);
            System.err.println(result.getData());
            return result;
        }else{
            throw new NullPointerException("没有对应的数据记录");
        }
    }
    //处理Java异常
    public JsonResult fallbackHandler(Long id,Throwable e){
        JsonResult result = new JsonResult<>(444,"NullPointerException异常");
        return result;
    }

    //处理Sentinel限流
    public JsonResult blockHandler(Long id, BlockException e){
        JsonResult result = new JsonResult<>(445,"BlockException限流");
        return result;
    }
}

我们来访问http://localhost:8084/consumer/fallback/4看效果:

Spring Cloud Alibaba - Sentinel(二)_第33张图片

六、Sentinel持久化配置

        在Sentinel Dashboard中配置规则之后重启应用就会丢失,所以实际生产环境中需要配置规则的持久化实现,Sentinel提供多种不同的数据源来持久化规则配置,包括file,redis、nacos、zk。

6.1、Sentinel规则持久化到Nacos

        将限流规则持久化进Nacos保存,只要刷新某个接口地址,Sentinel控制台的流控规则就能感应到,同时只要Nacos里面的配置不删除,Sentinel的流控规则就持续有效。

Spring Cloud Alibaba - Sentinel(二)_第34张图片

其实就是实现Sentinel Dashboard与Nacos之间的相互通信

 通过Nacos配置文件修改流控规则---拉取--->Sentinel Dashboard界面显示最新的流控规则

注意:在Nacos控制台上修改流控制,虽然可以同步到Sentinel Dashboard,但是Nacos此时应该作为一个流控规则的持久化平台,所以正常操作过程应该是开发者在Sentinel Dashboard上修改流控规则后同步到Nacos,遗憾的是目前Sentinel Dashboard不支持该功能。

具体操作

1、引入依赖


    com.alibaba.csp
    sentinel-datasource-nacos
    1.8.1

2、配置YML

# 端口号
server:
  port: 8401
# 服务名
spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        # nacos注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
      web-context-unify: false
      datasource: # 配置Sentinel的持久化
        nacos:
          nacos:
            serverAddr: localhost:8848
            groupId: DEFAULT_GROUP
            dataId: s1-sentinel.json
            ruleType: flow
  profiles:
    active: dev

3、进入到Nacos控制台,添加配置

[   
    {
        "resource": "/testA",
        "limitApp": "default",
        "grade": 1,
        "count": 2,
        "strategy": 0,
        "controlBehavior": 0
        "clusterMode": false
    }
]
---------------具体内容含义-----------------
resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。

Spring Cloud Alibaba - Sentinel(二)_第35张图片

 测试

Spring Cloud Alibaba - Sentinel(一)

Spring Cloud OpenFeign

干我们这行,啥时候懈怠,就意味着长进的停止,长进的停止就意味着被淘汰,只能往前冲,直到凤凰涅槃的一天!

你可能感兴趣的:(sentinel,spring,java)