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

SpringCloud Alibaba Sentinel实现熔断与限流

程序员文章站 2022-07-15 09:30:11
...

博客参考学习视频: https://www.bilibili.com/video/BV18E411x7eT?from=search&seid=4388336378730572330

上一篇SpringCloud Alibaba Nacos 服务注册和配置中心

SpringCloud Alibaba Sentinel实现熔断与限流

一、Sentinel

① 官网

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

② 是什么

SpringCloud Alibaba Sentinel实现熔断与限流

一句话解释, 之前我们讲解过的 Hystrix

③ 去哪下

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

SpringCloud Alibaba Sentinel实现熔断与限流

④ 能干嘛

SpringCloud Alibaba Sentinel实现熔断与限流

⑤ 怎么玩

https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel%E6%9C%8D%E5%8A%A1%E4%BD%BF%E7%94%A8%E4%B8%AD%E7%9A%84%E5%90%84%E7%A7%8D%E9%97%AE

服务使用中的各种问题

  1. 服务雪崩
  2. 服务降级
  3. 服务熔断
  4. 服务限流

二、安装 Sentinel 控制台

① sentinel 组件由 2 部分组成

SpringCloud Alibaba Sentinel实现熔断与限流

  • 后台
  • 前台 8080

② 安装步骤

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

下载到本地 sentinel-dashboard-1.8.0.jar

运行命令

  1. 前提:
  • java8 环境 OK
  • 8080 端口不能被占用
  1. 命令:
  • java -jar sentinel-dashboard-1.8.0.jar

SpringCloud Alibaba Sentinel实现熔断与限流

访问 sentinel 管理界面

  • http://localhost:8080
  • 登录账号密码均为 sentinel

SpringCloud Alibaba Sentinel实现熔断与限流

三、初始化演示工程

① 启动 Nacos8848 成功

  • http://localhost:8848/nacos/#/login

SpringCloud Alibaba Sentinel实现熔断与限流

② Module

建Module

  • cloudalibaba-sentinel-service8401

POM

<dependencies>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <!--SpringCloud ailibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- SpringBoot整合Web组件+actuator -->
        <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>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>

</project>

YML

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
    sentinel:
      transport:
        dashboard: localhost:8080 #配置Sentinel dashboard地址
        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: '*'

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

主启动

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

③ 启动 Sentinel8080

java -jar sentinel-dashboard-1.8.0

④ 启动微服务 8401

⑤ 启动 8401 微服务后查看 sentienl 控制台

  • 空空如也, 啥都没有

SpringCloud Alibaba Sentinel实现熔断与限流

Sentinel 采用的懒加载说明

  1. 执行一次访问即可

SpringCloud Alibaba Sentinel实现熔断与限流

结论

sentinel8080 正在监控微服务 8401

四、流控规则

① 基本介绍

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

② 流控模式

直接(默认)

  1. 直接->快速失败, 系统默认
  2. 配置及说明

SpringCloud Alibaba Sentinel实现熔断与限流

  1. 测试
  • 快速点击访问 http://localhost:8401/testA
  • 结果

SpringCloud Alibaba Sentinel实现熔断与限流

  • 思考: 直接调用默认报错信息, 技术方面 OK but, 是否应该有我们自己的后续处理?类似有一个 fallback 的兜底方法?

关联

  1. 是什么?

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

  2. 配置 A

SpringCloud Alibaba Sentinel实现熔断与限流

  1. postman 模拟并发密集访问 testB

SpringCloud Alibaba Sentinel实现熔断与限流

  • 访问 testB 成功

SpringCloud Alibaba Sentinel实现熔断与限流

postman 里新建多线程集合组

SpringCloud Alibaba Sentinel实现熔断与限流

将访问地址添加进新线程组
Run: 大批量线程高并发访问 B, 导致 A 失效了

  1. 运行后发现 testA 挂了
  • 点击访问 http://localhost:8401/testA

  • 结果: Blocked by Sentinel (flow limiting)

链路

链路模式针对的是上级接口,粒度控制的更细。

这个模式需要配合@SentinelResource注解使用,在资源上添加 @SentinelResource注解,表示这是一个资源,同时给出资源名。

新增TestService接口类:

public interface TestService {
    void message();
}

新增TestServiceImpl类,该类的message()方法上加上@SentinelResource(“message”)注解,表示资源名为messge:

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.example.mallorder.service.TestService;
import org.springframework.stereotype.Service;

@Service
public class TestServiceImpl implements TestService {

    @Override
    @SentinelResource("message")
    public void message() {
        System.out.println("message");
    }
}

修改TestController类,让两个测试方法都调用TestService提供的方法:

package com.oy.springcloud.config;


import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterContextConfig {
    @Bean
    public FilterRegistrationBean sentinelFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new CommonFilter());
        registration.addUrlPatterns("/*");
        // 入口资源关闭聚合
        registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
        registration.setName("sentinelFilter");
        registration.setOrder(1);
        return registration;
    }
}

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

此时的路口资源就是

  • /testA
  • /testB

当入口资源达到阈值的时候,就会启动此资源名为 message 的对应路口资源的流控。

坑来了,怎么解决?

禁止收敛URL的入口 context

从1.6.3 版本开始,Sentinel Web filter默认收敛所有URL的入口context,因此链路限流不生效。

1.7.0 版本开始(对应SCA的2.1.1.RELEASE),官方在CommonFilter 引入了WEB_CONTEXT_UNIFY 参数,用于控制是否收敛context。将其配置为 false 即可根据不同的URL 进行链路限流。

SCA 2.1.1.RELEASE之后的版本,可以通过配置spring.cloud.sentinel.web-context-unify=false即可关闭收敛,我们当前使用的版本是SpringCloud Alibaba 2.1.0.RELEASE,无法实现链路限流。

目前官方还未发布SCA 2.1.2.RELEASE,所以我们只能使用2.1.1.RELEASE,需要写代码的形式实现。

  1. 暂时将SpringCloud Alibaba的版本调整为2.1.1.RELEASE
  2. 配置文件中关闭sentinel的CommonFilter实例化

SpringCloud Alibaba Sentinel实现熔断与限流

  1. 添加一个配置类,自己构建CommonFilter实例
package com.oy.springcloud.config;


import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterContextConfig {
    @Bean
    public FilterRegistrationBean sentinelFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new CommonFilter());
        registration.addUrlPatterns("/*");
        // 入口资源关闭聚合
        registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
        registration.setName("sentinelFilter");
        registration.setOrder(1);
        return registration;
    }
}

分别通过两个路口资源进行访问,发现设置的入口资源被限流了。

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

③ 流控效果

直接->快速失败(默认的流控处理) :

  1. 直接失败, 抛出异常
Blocked by Sentinel (flow limiting)
  1. 源码
com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

预热:

  1. 说明

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

  2. 官网

    SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流默认 coldFactor 为 33. 默认 coldFactor 为 3, 即请求 QPS 从 threshold/3 开始, 经预热时长逐渐升至设定的 QPS 阈值。

限流冷启动 : https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81—%E5%86%B7%E5%90%AF%E5%8A%A8

  1. 源码

SpringCloud Alibaba Sentinel实现熔断与限流

  1. Warmup 配置

SpringCloud Alibaba Sentinel实现熔断与限流

  1. 多次点击 http://localhost:8401/testB

    刚开始不行, 后续慢慢 OK

    SpringCloud Alibaba Sentinel实现熔断与限流

  2. 应用场景

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

排队等待:

SpringCloud Alibaba Sentinel实现熔断与限流

  1. 匀速排队, 阈值必须设置为 QPS
  2. 官网

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

  1. 源码

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

  2. 测试

SpringCloud Alibaba Sentinel实现熔断与限流

五、降级规则

① 基本介绍

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

进一步说明

​ Sentinel 熔断降级会在调用链路中某个出现稳定的资源不稳定状态时(例如调用超时或异常比例高),对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联错误。

​ 当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。

Sentinel 的断路器是没有半开状态的

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

复习 Hystrix

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

② 降级策略实战

RT

  1. 是什么

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

  1. 测试

代码

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

    return "-----testD";
}

配置

注意:我这里更换了版本,有些配置选项不一样了。版本是1.7

SpringCloud Alibaba Sentinel实现熔断与限流

jmeter 压测

SpringCloud Alibaba Sentinel实现熔断与限流

结论:

​ 按照一秒钟打进来10个线程(大于5个了)调用testD ,我们希望200毫秒处理本次任务,如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了,后续我停止jmeter, 没有这么大的访问量,断路器关闭(保险丝恢复),微服务恢复OK。

异常比例

1. 是什么

SpringCloud Alibaba Sentinel实现熔断与限流

2. 测试

代码

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

配置

SpringCloud Alibaba Sentinel实现熔断与限流

Jmeter

SpringCloud Alibaba Sentinel实现熔断与限流

结论:

​ 按照上述配置,单独访问一次,必须来一次报错一次(int age = 10/0),调用一次错一次;

SpringCloud Alibaba Sentinel实现熔断与限流

开启jmeter后, 直接高并发发送请求,多次调用达到我们的配置条件了。断路器开启(保险丝跳闸),微服务不可用了,不在报错error而是服务降级了。

异常数

  1. 是什么

SpringCloud Alibaba Sentinel实现熔断与限流

  1. 异常数是按照分钟统计的
  2. 测试
@GetMapping("/testF")
public String testF(){
    log.info("testF 测试异常数");
    int age = 10/0;
    return "------testF 测试异常数";
}

配置

​ http://localhost:8041/testF, 第一次访问决定报错,因为除数不能为零,我们看到 error 窗口,但是达到5次报错后,进入熔断后降级。

SpringCloud Alibaba Sentinel实现熔断与限流

六、热点 key 限流

① 基本介绍

是什么:

SpringCloud Alibaba Sentinel实现熔断与限流

② 官网

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

热点参数限流

③ 承上启下复习 start

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

结论: 从 HystrixCommand@SentinelResource

④ 代码

  /**
   *  热点 key 限流
   */
@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){
  return "----testHotKey";
}

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

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

⑤ 配置

​ @SentinelResource(value = “testHotKey”), 异常打到了前台用户界面看不到,不友好

​ @SentinelResource(value = "testHotKey ", blockHandler = “deal_testHotKey”), 方法 testHostKey 里面的一个参数只要 QPS 超过每秒 1 次,马上降级处理。用了我们自己定义的 blockHandler 。

SpringCloud Alibaba Sentinel实现熔断与限流

⑥ 测试

  • Error: http://localhost:8401/testHotKey?p1=abc
  • Error: http://localhost:8401/testHotKey?p1=abc&p2=33
  • Right: http://localhost:8401/testHotKey?p2=abc

⑦ 参数例外项

上述案例演示了第一个参数 p1,当 QPS 超过 1 秒 1 次点击后马上被限流

SpringCloud Alibaba Sentinel实现熔断与限流

  1. 普通

    超过 1 秒钟一个后, 达到阈值 1 后马上被限流

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

  3. 特例

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

    配置

    SpringCloud Alibaba Sentinel实现熔断与限流

测试

1. http://localhost:8401/testHotKey?p1=5 √  
2. http://localhost:8401/testHotKey?p1=3 ×  
3. 当 p1 等于 5 的时候, 阈值变为 200  
4. 当 p1 不等于 5 的时候, 阈值就是平常的 1  

前提条件

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

⑧ 其他

@SentinelResource

​ 处理的是 Sentinel 控制台配置的违规情况,有blockHandler 方法配置的兜底处理

RuntimeException

​ int age = 10/ 0, 这个是java 运行时报出的运行时异常RuntimeException, @SentinelException, @SentineResource不管

总结

@SentineIResource 主管配置出错,运行出错该走异常走异常

七、系统规则

① 是什么

https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81

SpringCloud Alibaba Sentinel实现熔断与限流

② 各项配置参数说明

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

③ 配置全局 QPS

SpringCloud Alibaba Sentinel实现熔断与限流

八、@SentinelResource

① 按资源名称限流+后续处理

  • 启动 Nacos 成功

SpringCloud Alibaba Sentinel实现熔断与限流

  • 启动 Sentinel 成功

SpringCloud Alibaba Sentinel实现熔断与限流

Module

​ cloudalibaba-sentinel-service8401

POM

<dependency>
	<groupId>com.atguigu.springcloud</groupId>
	<artifactId>cloud-api-commons</artifactId>
	<version>${project.version}</version>
</dependency>

YML

server:
  port: 8401

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

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

业务类 RateLimitController

@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 服务\n" + "不可用");
    }
}

主启动

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

配置流控规则

  1. 配置步骤

SpringCloud Alibaba Sentinel实现熔断与限流

  1. 图形配置和代码关系
  2. 表示 1 秒钟内查询次数大于 1, 就跑到我们自定义的处流, 限流 。

测试

  1. 1 秒钟点击 1 下, OK

SpringCloud Alibaba Sentinel实现熔断与限流

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

    限流发送

    SpringCloud Alibaba Sentinel实现熔断与限流

② 按照 Url 地址限流+后续处理

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

业务类 RateLimitController

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

访问一次

SpringCloud Alibaba Sentinel实现熔断与限流

Sentinel 控制台配置

SpringCloud Alibaba Sentinel实现熔断与限流

测试

  • 疯狂点击 http://localhost:8401/rateLimit/byUrl

  • 结果

    会返回Sentinel 自带的限流处理限流结果

    SpringCloud Alibaba Sentinel实现熔断与限流

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

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

④ 客户自定义限流处理逻辑

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

CustomerBlockHandler

SpringCloud Alibaba Sentinel实现熔断与限流

public class CustomerBlockHandler {

    public static CommonResult handleException(BlockedException exception){
        return new CommonResult(2020, "自定义限流处理信息....CustomerBlockHandler");
    }

    public static CommonResult handleException2(BlockedException exception){
        return new CommonResult(2020, "自定义限流处理信息2....CustomerBlockHandler--2");
    }
}

RateLimitController

import com.oy.springcloud.entities.CommonResult;
import com.alibaba.csp.sentinel.slots.block.BlockException;

public class CustomerBlockHandler {

    public static CommonResult handleException(BlockException exception){
        return new CommonResult(2020, "自定义限流处理信息....CustomerBlockHandler");
    }

    public static CommonResult handleException2(BlockException exception){
        return new CommonResult(2020, "自定义限流处理信息2....CustomerBlockHandler--2");
    }
}

启动微服务后先调用一次

  • http://localhost:8401/rateLimit/customerBlockHandler

SpringCloud Alibaba Sentinel实现熔断与限流

Sentinel 控制台配置

  • 测试后我们自定义的出来了

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

  • 进一步说明

SpringCloud Alibaba Sentinel实现熔断与限流

⑤ 更多注解属性说明

SpringCloud Alibaba Sentinel实现熔断与限流

@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)。

官网:https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81

多说一句

SpringCloud Alibaba Sentinel实现熔断与限流

Sentinel 主要有三个核心 API

  1. SphU 定义资源
  2. Tracer 定义统计
  3. ContextUtil 定义了上下文

九、服务熔断功能

① sentinel 整合 ribbon+openFeign+fallback

② Ribbon 系列

1. 启动 nacos 和 sentinel,提供者 9003/9004

新建 cloudalibaba-provider-payment9003/9004

POM

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

    <artifactId>cloudalibaba-provider-payment9003</artifactId>
    <dependencies>
        <!--SpringCloud openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--SpringCloud ailibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.oy</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>
</project>

YML: 记得修改不同的端口号

server:
  port: 9003

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

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

主启动

@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<>();

    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,"frommysql,serverPort: "+serverPort,payment);
        return result;
    }
}

测试

地址: http://localhost:9003/paymentSQL/1

SpringCloud Alibaba Sentinel实现熔断与限流

2.消费者 84

新建 cloudalibaba-consumer-nacos-order84

POM

<dependencies>
        <!--SpringCloud openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--SpringCloud ailibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</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>
</project>

YML

server:
  port: 84


spring:
  application:
    name: nacos-order-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-payment-provider

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

主启动

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

业务类

  1. ApplicationContextConfig

SpringCloud Alibaba Sentinel实现熔断与限流

@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}
  1. CircleBreakerController 的全部源码
@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);
    }

}

修改后重启微服务

  • 热部署对java代码级生效即使
  • 对@SentinelResource注解内属性,有时效果不好

目的

  • fallback 管运行异常
  • blockHandler 管配置违规

测试地址

  • http://localhost:84/consumer/fallback/1

SpringCloud Alibaba Sentinel实现熔断与限流

没有任何配置

@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") //没有配置
    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);
    }

}

给客户 error 页面, 不友好

SpringCloud Alibaba Sentinel实现熔断与限流

只配置 fallback

编码(那个业务类下面的 CircleBreakerController 的全部源码)

@SentinelResource(value = "fallback",fallback = "handlerFallback")

SpringCloud Alibaba Sentinel实现熔断与限流

只配置 blockHandler

编码(那个业务类下面的 CircleBreakerController 的全部源码)

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

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

fallback 和 blockHandler 都配置

结果

SpringCloud Alibaba Sentinel实现熔断与限流

忽略属性…

编码(那个业务类下面的 CircleBreakerController 的全部源码)
图说

SpringCloud Alibaba Sentinel实现熔断与限流

3.Feign 系列

修改 84 模块

POM

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

YML

server:
  port: 84


spring:
  application:
    name: nacos-order-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-payment-provider

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

业务类

SpringCloud Alibaba Sentinel实现熔断与限流

  1. 带@FeignClient 注解的业务接口
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {
    @GetMapping("/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
  1. fallback=PaymentFallbackService.class
@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 的功能

@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class OrderNacosMain84 {
     public static void main(String[] args) {
           SpringApplication.run(OrderNacosMain84.class, args);
      }
}
  • http://localhost:84/consumer/paymentSQL/1

SpringCloud Alibaba Sentinel实现熔断与限流

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

4.熔断框架比较

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流

十、规则持久化

① 是什么

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

② 怎么玩

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

③ 步骤

修改 cloudalibaba-sentinel-service8401

POM

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

YML

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
    sentinel:
      transport:
        dashboard: localhost:8080 #配置Sentinel dashboard地址
        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: '*'

feign:
  sentinel:
    enabled: true # **Sentinel对Feign的支持
  • 添加 Nacos 数据源配置
spring:
  cloud:
        sentinel:
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

添加 Nacos 业务规则配置

SpringCloud Alibaba Sentinel实现熔断与限流

内容解析

[
    {
        "resource": "/rateLimit/byUrl",
        "limitApp": "default",
        "grade": 1,
        "count": 1,"strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]

SpringCloud Alibaba Sentinel实现熔断与限流

启动 8401 后刷新 sentinel 发现业务规则有了

SpringCloud Alibaba Sentinel实现熔断与限流

快速访问测试接口

  1. http://localhost:8401/rateLimit/byUrl
  2. 默认

SpringCloud Alibaba Sentinel实现熔断与限流

  1. 停止 8401 再看 sentinel

SpringCloud Alibaba Sentinel实现熔断与限流

  1. 重新启动 8401 再看 sentinel
  • 乍一看还是没有, 稍等一会儿 , 多次调用: http://localhost:8401/rateLimit/byUrl , 重新配置出现了, 持久化验证通过
    SpringCloud Alibaba Sentinel实现熔断与限流
相关标签: SpringCloud