Spring Cloud Alibaba 之 Sentinel下 控制台基本使用以及SentinelResource注解的使用


该文章都是在spring cloud alibaba 的基础上运用的

本地Demo

1.引入依赖

如果您的应用使用了 Maven,则在 pom.xml 文件中加入以下代码即可:

		<!--SpringCloud ailibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

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

完成后,项目即可被sentinel服务端所监控

spring cloud 项目所依赖内容如下:

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- spring cloud 依赖 -->
		<dependency>
			<groupId>com.alibaba.cloud</groupId>
			<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
		</dependency>

		<!-- sentinel依赖 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
        </dependency>
    </dependencies>

application.yml文件内容:

server:
  port: 8401 #服务端口

spring:
  application:
    name: cloudalibaba-sentinel-service #服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # nacos服务地址
    sentinel:
      transport:
        # 默认8719端口,键入被占用会自动从8719+1,直到找到未被占用的端口
        port: 8719
        # 配置Sentinel dashboard地址
        dashboard: localhost:8080 


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


创建DataController为外部提供访问,内容如下

@RestController
public class FlowLimitController {


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

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

启动后,先直接访问flowLimit1 和flowLimit2 两个接口,
在这里插入图片描述

在这里插入图片描述

访问成功后,刷新sentinel服务端即可监听到服务
在这里插入图片描述

二、控制台的基本使用

1.流量监控

1.1流控规则-直接

在这里插入图片描述

参数说明
  1. 资源名:唯一名称,默认请求路径
  2. 针对来源:Sentinel 可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
  3. 阀值类型/单机阀值
    • QPS(每秒的请求数量):当调用该API的QPS达到阀值的时候就会进行限流
    • 并发线程数:当调用该API的线程数量达到阈值的时候,进行限流
  4. 是否集群:当前不需要集群
  5. 流控模式:
    • 直接:API达到限流条件时,直接限流
    • 关联:当关联的资源达到阈值时,就限流自己
    • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)(API级别的针对来源)
  6. 流控效果:
    • 快速失败:直接失败,抛异常
    • Wam Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFacotor,经过预热时长,才达到设置的QPS阈值
    • 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
具体操作
QPS直接失败案例
  1. 添加有两种方式,可以直接在流控规则选项中添加,也可以在簇点链路中添加,一般会采取第二种方式
    在这里插入图片描述

  2. 现在我们给"/flowLimit2"添加流控。
    在这里插入图片描述

  3. 这里的意思就是我们现在单机阈值设定为1,代表的是当前这个接口只能被1秒访问一次,超过这个阈值,就会被Sentinel阻塞,现在默认为直接失败,也就是会在前台有一个体现
    在这里插入图片描述

线程数直接失败案例
  1. QPS和并发线程数规则详解
    在这里插入图片描述
  2. 那我们要演示这种效果,我们就需要让一个线程再进来办理的时候需要5秒,但是这个时候后面的线程也在疯狂的访问,所以后面的线程就会不会生效。
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
public class FlowLimitController {
    @GetMapping("/flowLimit3")
    public String flowLimit3(){
        //暂停5秒
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "-----flowLimit3";
    }
}
  1. 这个时候我们重启项目,然后重新通过访问flowLimit3接口,通过两个网页(线程)来快速访问,这个时候我们来看效果,这里要注意,要重新添加流控规则。

在这里插入图片描述

注意:这个时候虽然效果一致,但是是两种完全不同的规则,一种是QPS,一种是并发线程,这点大家一定要分清!

1.2 流控规则-关联

官方解释: 当关联的资源达到阈值时, 就限制自己
同属的来说,比如我们的程序,现在有flowLimit1flowLimit2 接口,当1关联的资源2达到阈值时,就限制自己,也就是2达到阈值,限制1本身。就好像我们家的孩子在外面打架,家长来处理一样,换做电商系统中,支付系统达到阀值,就限流下单系统。

具体演示

​ 当关联资源**/flowLimit1的qps阈值超时1时,就限流/flowLimit2**的Rest访问地址,当关联资源到阈值后限制配置好的资源名,首先我们先把FlowLimitController接口恢复原样
在这里插入图片描述

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

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

在这里插入图片描述
启动后进行访问flowLimit1接口则会返回“Blocked by Sentinel (flow limiting)”字样的信息
在这里插入图片描述

1.3 流控规则-链路

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

比如在一个微服务中,两个接口都调用同一个Service中的方法,并且该方法用SentinelResource(用于定义资源) 注解标注了,然后对该注解的资源(方法)进行配置,则可以选择链路模式。
在这里插入图片描述

具体演示

首先编写一个Service

首先我们编写一个Service

//service.TestService
@Service
public class TestService {
    // 定义限流资源
    @SentinelResource("common")
    public String common(){
        return "common";
    }
}

然后更改接口调用这个Service方法

@RestController
public class FlowLimitController {
    @Autowired
    TestService testService;
    
    @GetMapping("/flowLimit1")
    public String flowLimit1(){
        return testService.common();
    }

    @GetMapping("/flowLimit2")
    public String flowLimit2(){
        return testService.common();
    }
}

接下来配置流控规则:

这里要注意不要对/flowLimit1或者/flowLimit2进行限流规则的配置,要给用SentinelResource注解标注的资源进行配置限流规则,这里的意思为当我们用入口资源访问被SentinelResource注解标注的资源方法时,当超过阈值就会被限流,但是此时实际效果是没有效果。
在这里插入图片描述

在这里插入图片描述

没有效果的原因是因为我们还需要添加配置,让Sentinel 源码中 CommonFilter 中的 WEB_CONTEXT_UNIFY 参数为 false,将其配置为 false 即可根据不同的URL 进行链路限流,如果不配置将不会生效。

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        # 配置Sentinel dashboard地址
        dashboard: localhost:8080
        # 默认8719端口,键入被占用会自动从8719+1,直到找到未被占用的端口
        port: 8719
      # 配置为false
      web-context-unify: false

最后这个时候我们再来频繁的访问/flowLimit2接口,就会出现异常的情况,这也是流量效果快速失败在链路上的体现,是直接抛出异常。
在这里插入图片描述

1.4 流控规则-预热

官网手册地址:https://sentinelguard.io/zh-cn/docs/flow-control.html

概念:Warm Up方式,即预热/冷启动方式。该方式主要用于系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮的情况。

​ 预热公式:阈值/coldFactor(默认值为3),经过预热时间后才会达到阈值。

​ 冷启动的过程系统允许通过的QPS曲线如下图:
在这里插入图片描述
简单理解:

在这里插入图片描述

使用场景:一般秒杀系统中会有这样的流控设置,为了防止秒杀瞬间造成系统崩溃。

案例

​ 默认coldFactor为3,当发起请求即请求QPS从(阈值/3)开始,经过多长预热时长才逐步升至设定的QPS阈值,当前阈值设置为10,预热时长设置为5秒。

​ 最终的效果,系统初始化时阈值/3约等于3,即阈值在此时为3,经过5秒后阈值才慢慢升高到10

首先我们先来设置流控效果:

在这里插入图片描述

测试,我们用最简单的方法进行测试,直接在浏览器上手动刷新,然后我们来看Sentinel的实时监控
在这里插入图片描述

2.流控效果

2.1流控效果-排队等待

​ 官方文档:https://sentinelguard.io/zh-cn/docs/flow-control.html

​ 概念:匀速排队方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。

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

​ 例图:

在这里插入图片描述
####匀速器

​ 它的中心思想是,以固定的间隔时间让请求通过。当请求到来的时候,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过。否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过(排队等待处理);若预期的通过时间超出最大排队时长,则直接拒接这个请求。
在这里插入图片描述

​ Sentinel 匀速排队等待策略是漏桶算法结合虚拟队列等待机制实现的。

​ 注意:匀速排队模式暂时不支持 QPS > 1000 的场景。

演示

流控规则:

在这里插入图片描述
为了看到效果,我们在代码中进行打印,更改8401微服务中的FlowLimitController

package com.example.cloudalibabasentinel8401.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.example.cloudalibabasentinel8401.service.TestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
public class FlowLimitController {
    @Autowired
    TestService testService;
    @GetMapping("flowLimit1")
    public String flowLimit1(){
        log.info(Thread.currentThread().getName()+":testA");
        return testService.common();
    }

    @GetMapping("flowLimit2")
    public String flowLimit2(){
        return testService.common();
    }
}

最后我们可以通过Postman来进行测试,发送请求时没有延迟,同时发送10条请求,然后我们会发现就是排队效果1秒执行一个请求,同时我们在Idea中也可以看到打桩效果
在这里插入图片描述

3.Sentinel 熔断降级简介

基本介绍

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

在这里插入图片描述

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

熔断降级简介

​ 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版本对熔断降级做了大的调整,可以定义任意时长的熔断时间,引入了半开启恢复支持。下面梳理下相关特性。

熔断状态有三种状态,非别为OPEN、HALF_OPEN、CLOSED

状态说明
OPEN表示熔断开启,拒绝所有请求
HALF_OPEN探测恢复状态,如果接下来的一个请求顺利通过则表示结束熔断,否则继续熔断
CLOSE表示熔断关闭,请求顺利通过

熔断规则

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

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 引入)

官方文档网址:https://sentinelguard.io/zh-cn/docs/circuit-breaking.html

3.1Sentinel 熔断策略-慢调用

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

换而言之: 统计时长内线程数>最小请求数 每个线程响应时间>最大RT的线程数 占比 需要>比例阀值 才会出发熔断,且时间为熔断时长的值
在这里插入图片描述

​ 简单理解:

在这里插入图片描述
举例:

在这里插入图片描述

案例演示

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

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

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

在这里插入图片描述

测试

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

在这里插入图片描述

3.2 Sentinel 熔断策略-异常比例

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

换而言之: 统计时长内线程数>最小请求数 线程发生错误的占比 > 比例阀值 则会出发熔断机制,且时间为熔断时长的值

​ 注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。

在这里插入图片描述

简单理解:
在这里插入图片描述

案例

编写测试接口

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

设置熔断策略异常比例

在这里插入图片描述

测试

我们通过JMeter来测试,设定HTTP请求地址
在这里插入图片描述
1秒钟发送10个请求

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

3.3 Sentinel 熔断策略-异常数

概念:异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

​ 注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。

在这里插入图片描述

简单理解:
在这里插入图片描述

案例演示

编写接口

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

设置异常数策略,当1秒钟内请求超过5并且异常数大约5个的时候触发熔断
在这里插入图片描述

测试

通过JMeter来测试
在这里插入图片描述

1秒钟发送10个请求

在这里插入图片描述
此时就会触发熔断
在这里插入图片描述

4. Sentinel 热点规则

概念

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

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

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

官网:https://sentinelguard.io/zh-cn/docs/parameter-flow-control.html

在这里插入图片描述

在这里插入图片描述

这里还有相对应的高级选项,我们这里先了解基本规则。

4.1 使用@SentinelResource注解

其实这个热点限流其实就是更加细粒度的流控规则,那么如果想使用它就必须要配合对应SentinelResource注解。

Sentinel 提供了 @SentinelResource 注解用于定义资源,它有很多的参数,我们这里主要关注两个参数:

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

案例讲解

在这里插入图片描述

@SentinelResource(value=“xxx”)

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

 @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";
    }

然后再来配置热点规则

在这里插入图片描述

这里要说明一下,参数索引0实际上代表的就是我们设置的hot1参数

在这里插入图片描述

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

在这里插入图片描述

@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 = "hot13",required = false) String hot3){
    return "----testHotKey";
}

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

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

在这里插入图片描述

参数例外项

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

​ **注意:**参数例外项中的参数类型仅支持一下7种数据类型
在这里插入图片描述
案例演示

当前我们需要让hot1的值为5的时候阈值可以达到200,首先Sentinel页面中修改对应热点规则(在这之前,先演示传递一个参数,否则配置失败)

在这里插入图片描述

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

在这里插入图片描述

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

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

@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";
}
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<String> fallback(@PathVariable Long id){
        if(id<=3){
            //通过Ribbon发起远程访问,访问9003/9004
            JsonResult<String> 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非法)地址时,就会出现对应的显示效果:

在这里插入图片描述

明显此时显示效果非常不好,我们就可以通过@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<String> fallback(@PathVariable Long id){
        if(id<=3){
            //通过Ribbon发起远程访问,访问9003/9004
            JsonResult<String> result = restTemplate.getForObject(SERVICE_URL+"/info/"+id,JsonResult.class);
            System.err.println(result.getData());
            return result;
        }else{
            throw new NullPointerException("没有对应的数据记录");
        }
    }
    //保证方法签名基本保持一致,但是要添加异常类型参数
    public JsonResult<String> fallbackHandler(Long id,Throwable e){
        JsonResult<String> result = new JsonResult<>(444,"出现未知商品id");
        return result;
    }
}

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

4.2 同时配置blockHandler和fallback属性

通过上述的内容,我们很清楚的知道了fallback属性的作用,但是大家现在想一个问题,如果我们在使用@SentinelResource属性的时候,同时设置blockHandler属性和fallback属性时,并且同时出现了Sentinel异常和Java异常,这个时候会执行哪个方法那。

我们还是回顾一下blockHandler属性的概念:

  • blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

案例演示

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

@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")
    public JsonResult<String> fallback(@PathVariable Long id){
        if(id<=3){
            //通过Ribbon发起远程访问,访问9003/9004
            JsonResult<String> result = restTemplate.getForObject(SERVICE_URL+"/info/"+id,JsonResult.class);
            System.err.println(result.getData());
            return result;
        }else{
            throw new NullPointerException("没有对应的数据记录");
        }
    }
    //处理Java异常
    public JsonResult<String> fallbackHandler(Long id,Throwable e){
        JsonResult<String> result = new JsonResult<>(444,"NullPointerException异常");
        return result;
    }

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

此时我们来设置Sentinel配置,我们通过熔断规则中的异常数来演示(当然也可以用其他的)

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

在这里插入图片描述

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

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

在这里插入图片描述

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

在这里插入图片描述

4.3 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<String> fallback(@PathVariable Long id){
        if(id<=3){
            //通过Ribbon发起远程访问,访问9003/9004
            JsonResult<String> result = restTemplate.getForObject(SERVICE_URL+"/info/"+id,JsonResult.class);
            System.err.println(result.getData());
            return result;
        }else{
            throw new NullPointerException("没有对应的数据记录");
        }
    }
    //处理Java异常
    public JsonResult<String> fallbackHandler(Long id,Throwable e){
        JsonResult<String> result = new JsonResult<>(444,"NullPointerException异常");
        return result;
    }

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

5.Sentinel 系统规则

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

系统规则

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

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

在这里插入图片描述

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

  • 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来进行测试,直接设置规则

在这里插入图片描述

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

在这里插入图片描述

@SentinelResource 自定义限流逻辑处理

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

案例复习

之前我们用过这个注解,同时了解了它的两个属性:

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

我们之前利用这个注解完成了热点规则的学习,同时做了一个案例,我们简单复习一下,这个案例的核心思想就是我们传递一个指定参数,然后通过注解@SentinelResource注解标注资源进行限流,当出现限流以后,通过blockHandler属性设置限流以后的解决方法。

在这里插入图片描述

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

@SentinelResource 资源限流

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

编写一个新的控制器类型SentinelResourceTestController,使用@SentinelResource注解同时使用blockHandler属性

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

public String handler_resource(BlockException exception){
    return "系统繁忙";
}

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

在这里插入图片描述

具体规则
在这里插入图片描述

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

@SentinelResource URL限流

**核心点:**使用@SentinelResource注解,但是不使用blockHandler属性,系统会调用默认限流异常处理方法。

其实这个注解,我们还可以更换请求地址为资源,比如我们在新建一个测试接口方法

@GetMapping("/byRest")
@SentinelResource(value = "byRest")
public String byRest(){
    return "-----byRest";
}

给这个接口地址添加流控

在这里插入图片描述

此时如果没有自己定义限流处理方法,会走系统默认的

在这里插入图片描述

结论

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

自定义限流处理逻辑

其实我们在使用@SentinelResource注解这两种方案的时候,会出现一些问题:

  1. 没有体现我们自己的业务要求。
  2. 自定义处理方法和业务代码耦合在一起。
  3. 每个业务方法都添加一个限流处理方法,代码将会加剧膨胀。
  4. 无法实现统一全局处理。

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

具体逻辑

第一步

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

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

第二步

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

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

第三步

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

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

对应关系图

在这里插入图片描述

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 环境搭建和项目创建 首先,我们需要确保已经安装了JDK和Maven,然后创建一个Spring Boot项目,并添加相关的依赖。 在pom.xml文件中,添加如下依赖: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>0.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>0.2.1.RELEASE</version> </dependency> 这里使用了nacos作为注册中心和配置中心,sentinel作为限流熔断组件。 2. Nacos配置中心使用 创建一个配置文件,取名为bootstrap.yml,内容如下: spring: cloud: nacos: discovery: server-addr: localhost:8848 config: server-addr: localhost:8848 file-extension: yaml group: EXAMPLE_GROUP prefix: /example 这里定义了nacos的注册中心和配置中心的地址,并指定了配置文件的格式及其所属的分组和路径前缀。 然后创建一个application.yml文件,用于配置应用的参数。 示例: spring: application: name: example-service server: port: 8080 定义了应用的名称和端口号。 3. Nacos注册中心使用 在启动类上添加@EnableDiscoveryClient注解,表示使用nacos作为注册中心。 示例: @SpringBootApplication @EnableDiscoveryClient public class ExampleServiceApplication { public static void main(String[] args) { SpringApplication.run(ExampleServiceApplication.class, args); } } 4. Nacos配置中心读取配置 使用@Value注解来读取nacos配置中心中的配置。 示例: @Service public class ExampleService { @Value("${example.config}") private String config; public String getConfig() { return config; } } 这里配置中心中的key为example.config,可以在nacos控制台中进行添加和修改。 5. Sentinel限流熔断使用 在启动类上添加@EnableCircuitBreaker和@EnableFeignClients注解,表示启用熔断和使用Feign进行http调用。 示例: @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker @EnableFeignClients public class ExampleServiceApplication { public static void main(String[] args) { SpringApplication.run(ExampleServiceApplication.class, args); } } 在使用Feign进行http调用的接口上添加@FeignClient和@SentinelResource注解。 示例: @FeignClient(name = "example-service") public interface ExampleClient { @RequestMapping("/test") @SentinelResource("test") String test(); } 这里定义了一个FeignClient,调用了example-service服务的/test接口,并使用@SentinelResource注解指定了资源名称,用于进行限流和熔断。 6. 总结 至此,spring cloud alibaba的基础使用已经介绍完毕,通过nacos作为注册中心和配置中心,sentinel作为限流熔断组件,我们可以快速地开发一个分布式服务的系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值