微服务架构 | 5.2 基于 Sentinel 的服务限流及熔断

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
简介: Sentinel 是面向分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从限流、流量整形、服务降级、系统负载保护等多个维度来帮助我们保障微服务的稳定性;

前言

参考资料
《Spring Microservices in Action》
《Spring Cloud Alibaba 微服务原理与实战》
《B站 尚硅谷 SpringCloud 框架开发教程 周阳》
《Sentinel GitHub 官网》
《Sentinel 官网》

Sentinel 是面向分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从限流、流量整形、服务降级、系统负载保护等多个维度来帮助我们保障微服务的稳定性;


1. Sentinel 基础知识

1.1 Sentinel 的特性

  • 丰富的应用场景:几乎涵盖所有的应用场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制等;
  • 实时监控:开发者可以在控制台中看到接入应用的单台机器秒级数据,甚至500台以下规模的集群汇总运行情况;
  • 开源生态支持:Sentine提供开箱即用的与其他开源框架/库的整合,例如与Spring Cloud、Dubbo、gRPC的整合;
  • SPI 扩展点支持:提供了 SPI 扩展点支持,开发者可以通过扩展点来定制化限流规则,动态数据源适配等需求;

Sentinel 的特性
Sentinel 的开源生态

1.2 Sentinel 的组成

  • 分两部分:

    • 核心库(Java 客户端):不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo、Spring Cloud 等框架也有较好的支持;
    • 控制台(Dashboard):亦称 Sentinel 服务器。基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器;

1.3 Sentinel 控制台上的 9 个功能

功能 说明
实时监控 实时监控每个资源名(接口、请求路径)的通过 QPS、拒绝 QPS 和响应时间;
簇点链路 通过树状视图和列表视图展示接口调用的关系以及通过 QPS、拒绝 QPS、并发数、平均 RT、分钟通过和拒绝等信息;
流控规则 又称:流量控制(flow control)。其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性;
熔断规则 对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩;
热点规则 又称:热点参数限流规则。热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制;
系统规则 系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性;
授权规则 根据调用来源来判断该次请求是否允许放行;
集群流控 集群流控可以解决流量不均匀导致总体限流效果不佳的问题;
机器列表 收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线;

Sentinel 控制台上的 9 个功能

1.4 Sentinel 工作原理

  • Sentinel 的核心分为三部分:工作流程、数据结构和限流算法;

Sentinel 工作原理

  • 调用链路是 Sentinel 的工作主流程,由各个 Slot 插槽组成,将不同的 Slot 按照顺序串在一起(责任链模式),从而将不同的功能(限流、降级、系统保护)组合在一起;
  • Sentinel 中各个 Slot 承担了不同的职责,如:LogSlot 负责记录日志、StatisticSlot 负责统计指标数据、FlowSlot 负责限流等。这是一种职责分离的设计,每个模块更聚焦于实现某个功能;
  • 在 Sentinel 中,所有的资源都对应一个资源名称(resourceName),每次访问该资源都会创建一个 Entry 对象,在创建 Entry 的同时,会创建一系列功能槽(Slot Chain),这些槽会组成一个责任链,每个槽负责不同的职责;
Slot 插槽 说明
NodeSelectorSlot 负责收集资源的调用路径,以树状结构存储调用栈,用于根据调用路径来限流降级;
ClusterBuilderSlot 负责创建以资源名维度统计的 ClusterNode ,以及创建每个 ClusterNode 下按调用来源 origin 划分的 StatisticNode;
LogSlot 在出现限流、熔断、系统保护时负责记录日志;
AuthoritySlot 权限控制,支持黑名单和白名单两种策略;
SystemSlot 控制总的入口流量,限制条件依次是总 QPS、总线程数、RT 阈值、操作系统当前 load1、操作系统当前 CPU 利用率;
FlowSlot 根据限流规则和各个Node中的统计数据进行限流判断;
DegradeSlot 根据熔断规则和各个Node中的统计数据进行服务降级;
StatisticSlot 统计不同维度的请求数、通过数、限流数、线程数等 runtime 信息,这些数据存储在 DefaultNode、OriginNode 和 ClusterNode 中;

1.5 Sentinel 源码分析


2. 安装并运行 Sentinel 控制台

Sentinel 与 Nacos 类似,有两种安装方式:使用已经编译好的安装包和源码部署;由于[]()要对 Sentinel 源码进行分析,这里推荐源码部署;
这里选择的版本是 1.8.3;

2.1 安装包安装 Sentinel 控制台

2.1.1 下载 Sentinel

从官网下载 Sentinel

2.1.2 使用命令启动 Sentinel 控制台

  • 运行前要满足两个条件:有 java8 环境、8080 端口不能被占用;

    • win10 解决端口占用用以下三个命令即可(用管理员打开 cmd):
    • 查看所有端口:netstat -ano 或者指定查看 8080 端口占用情况 netstat -aon|findstr 8080
    • 根据进程查找应用程序:tasklist|findstr {上面查到的进程 PID}
    • 关闭进程:taskkill /f /t /im {上面查到的应用程序}
  • 到下载的 jar 包下启动 cmd 窗口,运行下面命令:
  • java -jar sentinel-dashboard-1.8.3.jar
  • 如果不想杀掉 8080 程序可以之地端口号运行:java -Dserver.port=7777 -Dcsp.sentinel.dashboard.server=localhost:7777 -jar sentinel-dashboard-1.8.3.jar

    • -Dserver.port:指定 Sentinel 控制台的访问端口,默认是 8080;
    • -Dcsp.sentinel.dashboard.server:指定 Sentinel Dashboard 控制台的 IP 地址和端口,这里进行设置的目的是把自己的限流数据暴露到监控平台;
    • -Dproject.name:设置项目名称;

打开 cmd 命令窗口
启动 Sentinel

2.1.3 访问 Sentinel 控制台

  • 发送请求:http://localhost:8080
  • 1.6.0 版本后引入登录功能,默认登录账号密码均为 sentinel

成功启动 sentinel

2.2 源码部署 Sentinel 控制台

2.2.1 拉取源码

拉取 Sentinel 源码

2.2.2 启动 Sentinel 控制台

  • mvn install 将项目安装到本地;
  • 接着找到 sentinel-dashboard 模块下的主程序类 DashboardApplication 运行即可;
  • *为了防止端口占用,这里笔者将 Sentinel 控制台的端口号改为了 28080;


3. Spring Cloud Nacos 集成 Sentinel

3.1 引入 pom.xml 依赖文件

<!-- Sentinel 核心包 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

3.2 修改 bootstrap.yml 配置文件

spring:
  application:
    name: nacos-config-client #必须,构成 Nacos 配置管理 Data ID 字段的一部分
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos 服务注册中心地址
      config:
        server-addr: localhost:8848 #Nacos 作为配置中心地址
        file-extension: yaml #指定 yaml 格式的配置
    #以下新增
    sentinel:
      transport:
        dashboard: localhost:28080  #配置Sentinel dashboard地址
        port: 8719  #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口

3.3 编写业务类

  • 编写个接口测试用;
  • 基于 Sentinel 控制台的流控规则不需要添加任何资源埋点;
  • 在默认情况下 Sentinel Starter 会对所有 HTTP 请求进行限流;
@RestController
public class ConfigClientController {
    @GetMapping("/easytest")
    public String testMothod(){
        return "test";
    }
}
  • 这时启动程序,访问 Sentinel 控制台,并不能看到效果。由于 Sentinel 使用的是懒加载机制,需要进行一次接口调用才能看到监控效果界面:

监控效果界面

3.4 基于 Sentinel 控制台添加容灾规则

  • 手动配置容灾规则请见本篇《5. 手动配置流控规则》;

3.4.1 流控规则页面说明

下图表示 1 秒钟内查询 1 次就是 OK,若超过次数 1,就直接-快速失败,报默认错误;

流控规则控制页面

  • 资源名 resource:唯一名称,默认请求路径;
  • 针对来源 limitApp:Sentinel 可以针对调用者进行限流,填写微服务名,默认 default(不区分来源);
  • 阈值类型 grade、单机阈值 count

    • QPS grade值1(每秒钟的请求数量):当调用该 API 的 QPS 达到阈值的时候,进行限流;
    • 线程数 grade值0:当调用该 API 的线程数达到阈值的时候,进行限流;
  • 是否集群 clusterMode:不需要集群;
  • 流控模式

    • 直接:API 达到限流条件时,直接限流;
    • 关联 strategy:当关联的资源 B 达到阈值时,就限流自己 A(这里需要手速快或使用 Postman 模拟高并发);
    • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)(API 级别的针对来源);
  • 流控效果 controlBehavior

    • 快速失败 RuleConstant.CONTROL_BEHAVIOR_DEFAULT:直接失败,抛异常 Blocked by Sentinel (flow limiting)
    • Warm Up(预热) RuleConstant.CONTROL_BEHAVIOR_WARM_UP:根据 codeFactor(冷加载因子,默认3)的值,阈值除以 codeFactor 得到预热时长。经过预热时长,才达到设置的 QPS 阈值。

      • 用于秒杀系统在开启的瞬间,刚开始不行,后续慢慢OK;
    • 排队等待 RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER:匀速排队,让请求以匀速的速度通过(漏桶算法),阈值类型必须设置为 QPS,否则无效;

      • 用于处理间隔性突发的流量;

3.4.2 熔断规则页面说明

下图表示当我们的请求响应超过 1000ms ,并且该统计的请求比例超过 50% 时(统计的请求数量需要大于 5),触发熔断;
经过熔断时长 5s 后进入探测恢复状态,若下一个请求响应时间小于 1000ms,则熔断结束;反之再次熔断;

熔断规则控制界面

  • 资源名 resource:唯一名称,默认请求路径;
  • 熔断策略 grade

    • 慢调用比例 (SLOW_REQUEST_RATIO):请求的响应时间大于 RT 统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断;
    • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%;
    • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断;
  • 最大 RT、比例阈值 count:慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例和异常数模式下为对应的阈值;
  • 慢调用比例阈值 slowRatioThreshold:仅慢调用比例模式有效(1.8.0 引入);
  • 熔断时长 timeWindow:单位为 s;
  • 最小请求数 minRequestAmount:请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入);
  • 统计时长 statIntervalMs:如 60*1000 代表分钟级(1.8.0 引入);

3.4.3 热点规则页面说明

  • 常用于:统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制;
  • 热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效;
  • Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式;

1. 简单示例

  • 编写业务类
  • @SentinelResource 的用法详情请见本篇第 4 点;
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "dealHandlerTestHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                         @RequestParam(value = "p2",required = false) String p2){
    return "testHotKey";
}
public String dealHandlerTestHotKey(String p1, String p2, BlockException exception) {
    return "dealHandler_testHotKey";
}
  • 在 Sentinel 控制台上配置规则
下图表示第一个参数(索引为0)有值的话(对应上述代码的 p1),1 秒的 QPS 为 1,超过就限流,限流后调用 dealHandler_testHotKey 支持方法;

热点规则控制界面

  • 资源名 resource:唯一名称,默认请求路径;
  • 限流模式 grade:限流模式只支持 QPS 模式;
  • 参数索引 paramIdx:必填,对应 SphU.entry(xxx, args) 中的参数索引位置;
  • 单机阈值 count:限流阈值,必填;
  • 统计窗口时长 durationInSec:单位为秒,1.6.0 版本开始支持;
  • 是否集群 clusterMode:是否是集群参数流控规则;
  • 集群流控相关配置 clusterConfig
  • 参数例外项 paramFlowItemList:可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型和字符串类型;
  • 流控效果 controlBehavior:流控效果(支持快速失败和匀速排队模式),1.6.0 版本开始支持;
  • 测试访问

    • http://localhost:18082/testHotKey?p1=22:触发限流;
    • http://localhost:18082/testHotKey?p1=22&p2=33:触发限流;
    • http://localhost:18082/testHotKey?p2=33:不触发限流;

2. 参数例外项配置

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

热点规则控制界面-参数例外配置

  • 注意点击“添加”;
  • 热点参数必须是基本类型或者 String;

3.4.4 系统规则页面说明

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

系统规则控制界面

  • load1 表示系统的负载;
  • 系统规则支持一下五种模式:

    • 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.4.5 授权规则页面说明

  • 根据调用来源来判断该次请求是否允许放行;

授权规则控制界面

  • 资源名 resource:资源名,即限流规则的作用对象;
  • 流控应用 limitApp:对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB;
  • 授权类型 strategy:限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式;


4. 使用 @SentinelResource 自定义限流处理逻辑

  • Sentinel starter 在默认情况下会为所有的 HTTP 服务提供限流埋点,所以如果只想对 HTTP 服务进行限流,那么只需要添加依赖即可,不需要修改任何代码;
  • 如果想要对特定的方法进行限流或者降级,则需要通过 @SentinalResouce 注解来实现限流资源的定义;

4.1 注解参数属性说明

  • @SentinelResource 注解参数属性说明:
public @interface SentinelResource {
    //资源名称,必需项(不能为空)
    String value() default "";
    
    //entry 类型,有 IN 和 OUT 两个选项,(默认为 EntryType.OUT)
    EntryType entryType() default EntryType.OUT;

    int resourceType() default 0;

    //对应处理 BlockException 的函数名称,可选项
    String blockHandler() default "";

    /** 
     * blockHandler 函数默认需要和原方法在同一个类中,如果希望使用其他类的函数,则需要指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析
    Class<?>[] blockHandlerClass() default {};
    
    /**
     * fallback 函数默认需要和原方法在同一个类中;
     * 若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象;
     * 对应的函数必需为 static 函数,否则无法解析;
     **/
    String fallback() default "";

    /**
     * 默认的 fallback 函数名称,可选项,通常用于通用的 fallback逻辑(即可以用于很多服务或方法);
     * 默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理;
     * 若同时配置了 fallback 和 defaultFallback,则只有 fallback会生效;
     * defaultFallback 函数签名要求:返回值类型必须与原函数返回值类型一致;方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常;
     * defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析;
     **/ 
    String defaultFallback() default "";

    /**
     * fallback 函数名称,可选项,用于在抛出异常的时候提供  fallback 处理逻辑;
     * fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理;
     * fallback 函数签名和位置要求:返回值类型必须与原函数返回值类型一致;方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常
     **/
    Class<?>[] fallbackClass() default {};
    
    //用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出
    Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};

    Class<? extends Throwable>[] exceptionsToIgnore() default {};
}
  • @SentinelResource:处理的是 Sentinel 控制台配置的违规情况,有 blockHandler 方法配置的兜底处理;
  • RuntimeException:如:int age = 10/0。这个是 java 运行时报出的运行时异常 RunTimeException,@SentinelResource 不管;
  • @SentinelResource 主管配置出错,运行出错走异常处理程序;

4.2 配置与代码的关系

图形配置和代码关系

4.3 自定义限流处理逻辑

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

在 handler 包下新建 CustomerBlockHandler 类;
public class CustomerBlockHandler{
    public static CommonResult handlerException(BlockException exception){
        return new CommonResult(4444,"按客戶自定义,global handlerException----1");
    }
    public static CommonResult handlerException2(BlockException exception){
        return new CommonResult(4444,"按客戶自定义,global handlerException----2");
    }
}

4.3.2 在 controller 接口上配置自定义逻辑

@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
        blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException2")
public CommonResult customerBlockHandler(){
    return new CommonResult(200,"按客户自定义限流处理逻辑");
}

代码对应

  • 在实际生产中,上述所有的代码都要用 try-catch-finally 方式进行处理;

4.3.3 在 Sentinel 控制台上配置

在 Sentinel 控制台上配置

5. 手动配置流控规则

  • 我们除了能在 Sentinel 控制台上配置流控规则外,还可以借助 Sentinel 的 InitFunc SPI 扩展接口来实现:

    • 需要实现自己的 InitFunc 接口;
    • 并在 init 方法中编写规则加载的逻辑;
  • 我们接着 3.4.3 的 controller 示例:

5.1 controller 接口

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "dealHandlerTestHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                         @RequestParam(value = "p2",required = false) String p2){
    return "testHotKey";
}
public String dealHandlerTestHotKey(String p1, String p2, BlockException exception) {
    return "dealHandler_testHotKey";
}

5.2 实现 InitFunc 接口

public class FlowRuleInitFunc implements InitFunc{
    @Override 
    public void init() throws Exception{
        List<FlowRule>rules=new ArrayList<>(); 
        FlowRule rule=new FlowRule(); 
        rule.setcount(1); 
        rule.setResource("testHotKey"); 
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS); 
        rule.setLimitApp("default"); 
        rules.add(rule); 
        FlowRuleManager.loadRules(rules);
}
  • SPI 是扩展点机制,如果需要被 Sentinel 加载,那么还要在 resource 目录下创建 META-INF/services/com.alibaba.csp.sentinelinit.InitFunc 文件,文件内容就是自定义扩展点 FlowRuleInitFunc 的全路径;


6. Sentinel 规则持久化

  • 一旦我们重启应用,Sentinel 规则将消失,生产环境需要将配置规则进行持久化;
  • 将限流配置规则持久化进 Nacos 保存,只要刷新 REST 地址,Sentinel控制台的流控规则就能看到,只要 Nacos 里面的配置不删除,针对 Sentinel 上的流控规则持续有效;

6.1 添加 pom.xml 依赖文件

<!-- nacos 配置中心 -->
<dependency>
   <groupId>com.alibaba.cloud</groupId>
   <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Sentinel 持久化相关 -->
<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

6.2 修改 bootstrap.yml 配置文件

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
        file-extension: yaml
    sentinel:
      transport:
        dashboard: localhost:28080
        port: 8719
      # 以下新增  
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848  #将规则保存进 Nacos 配置中心
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json #指定配置项的内容格式,可选:JSON、XML。如果需要自定义,则可以将值配置为 custom,并配置 converter-class 指向 converte r类;
            rule-type: flow #数据源中规则的类型,可选:flow、degrade、param-flow、gw-flow

6.3 在 Nacos 服务器上添加配置

  • /easytest 接口添加流控规则;

对 /easytest 接口添加流控规则

  • Resource:资源名称;
  • LimitApp:来源应用;
  • Grade:阈值类型,0表示线程数,1表示 QPS;
  • Count:单机阈值;
  • Strategy:流控模式,0表示直接,1表示关联,2表示链路;
  • ControlBehavior:流控效果,0表示快速失败,1表示 Warm Up,2表示排队等待;
  • ClusterMode:是否集群;

6.4 一些说明

  • 访问 Sentinel 控制台可能看不见配置,多次调用 /easytest 后才能看见配置规则;
  • 在 Sentinel 控制台上添加和修改配置不能同步到 Nacos 配置中心,服务重启后配置规则不能同步到 Nacos 上;
  • Nacos 在这当中扮演的角色应该是一个查询数据库,不建议在 Nacos 上修改流控规则;


7. Sentinel 控制台集成 Nacos 实现规则同步

  • 上面说过,在 Sentinel 控制台上添加和修改配置不能同步到 Nacos 配置中心,服务重启后配置规则不能同步到 Nacos 上;
  • 但我们可以自己实现这个功能,配置步骤如下:

7.1 修改依赖与配置文件

  • 修改 sentinel-dashboard 模块里的 pom.xml 依赖文件;
  • 注释掉 \<scrop>:
<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-datasource-nacos</artifactId>
  <!--<scope>test</scope>-->
<dependency>
  • 修改:src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html;里的 dashboard.flowV1 为 dashboard.flow;
  • 使之调用 FlowControllerV2 中的接口;
<li ui-sref-active="active" ng-if="!entry.isGateway">
  <!--<a ui-sref="dashboard.flowV1({app: entry.app})">-->
  <a ui-sref="dashboard.flow({app: entry.app})">
    <i class="glyphicon glyphicon-filter"></i>  流控规则</a>
</li>
  • application.yml 里添加 Nacos 服务器的配置信息:

    • sentinel.nacos.serverAddr=localhost:8848
    • sentinel.nacos.namespace=
    • sentinel.nacos.group-id=DEFAULT_GROUP

7.2 新建 Nacos 规则包,里面存放与同步的配置类

  • 新建包 com/alibaba/csp/sentinel/dashboard/rule/nacos/
  • 下面新建五个类,可以从这个目录下拷贝 src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos
  • 新建后的图:

新建五个类

7.2.1 新建 NacosPropertiesConfiguration 类

  • 在该包下新建 NacosPropertiesConfiguration.java 类,用来加载外部化配置;
@ConfigurationProperties(prefix="sentinel.nacos")
public class NacosPropertiesConfiguration {
    private String serverAddr;
    private String dataId;
    private String groupId = "DEFAULT_GROUP";
    private String namespace;
    //这里省略 get/set 方法
}    

7.2.2 新建 NacosConfiguration 类

  • 在该包下创建一个 Nacos 配置类 NacosConfiguration
@EnableConfigurationProperties(NacosPropertiesConfiguration.class)
@Configuration
public class NacosConfiguration {

    //Converter 转换器,将 FlowRuleEntity 转化成 FlowRule,以及反向转化
    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder(){
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder(){
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    //注入 Nacos 服务 ConfigService
    @Bean
    public ConfigService nacosConfigService(NacosPropertiesConfiguration nacosPropertiesConfiguration) throws NacosException {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosPropertiesConfiguration.getServerAddr());
        properties.put(PropertyKeyConst.NAMESPACE, nacosPropertiesConfiguration.getNamespace());
        return ConfigFactory.createConfigService(properties);
    }
}

7.2.3 新建 NacosConstants 类

  • 在该包下创建一个 Nacos 常量类 NacosConstants。分别表示默认的 GROUP_ID 和 DATA_ID 的后缀:
public class NacosConstants {
    public static final String DATA_ID_POSTFIX = "-sentinel-flow";
    public static final String GROUP_ID = "DEFAULT_GROUP";
}

7.2.4 新建 FlowRuleNacosProvider 类

  • 在该包下新建 FlowRuleNacosProvider 类实现动态从 Nacos 配置中心获取流控规则;
@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {

    private static Logger logger = LoggerFactory.getLogger(FlowRuleNacosProvider.class);

    @Autowired
    private NacosPropertiesConfiguration nacosConfigProperties;

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<String, List<FlowRuleEntity>> converter;

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        String dataID = new StringBuilder(appName).append(NacosConstants.DATA_ID_POSTFIX).toString();
        //通过ConfigServic.getConfig方法从Nacos Config Server中读取指定配置信息,并通过converter转化为FlowRule规则
        String rules = configService.getConfig(dataID, nacosConfigProperties.getGroupId(), 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

7.2.5 新建 FlowRuleNacosPublisher 类

  • 在该包下新建 FlowRuleNacosPublisher(流控规则发布类) 类,在 Sentinel 控制台 上修改完配置之后,需要调用该发布方法将数据持久化到 Nacos 中;
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {

    @Autowired
    private NacosPropertiesConfiguration nacosPropertiesConfiguration;

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<List<FlowRuleEntity>, String> converter;

    @Override
    public void publish(String appName, List<FlowRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(appName, "appName cannot be empty");
        if (rules == null) {
            return;
        }
        String dataID = new StringBuilder(appName).append(NacosConstants.DATA_ID_POSTFIX).toString();
        configService.publishConfig(dataID, nacosPropertiesConfiguration.getGroupId(), converter.convert(rules));
    }
}

7.3 修改 FlowControllerV2 类

  • 上面配置的两个类(FlowRuleNacosPublisher 与 FlowRuleNacosProvider)注入进来,表示规则的拉取和规则的发布统一用我们前面自定义的两个实例;

修改 FlowControllerV2 类

7.4 修改 Nacos 客户端

  • 对于应用程序来说,需要改动的地方比较少,只要注意配置文件中 data-id 的命名要以 -sentinel-flow 结尾即可,因为在 Sentinel Dashboard 中我们写了一个固定的后缀;
spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 
      config:
        server-addr: localhost:8848
        file-extension: yaml
    sentinel:
      transport:
        dashboard: localhost:28080 
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848 
            # 修改下面这条
            dataId: ${spring.appliction.name}-sentinel-flow   
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow


8. 自定义 URL 限流异常和 URL 资源清洗

8.1 自定义 URL 限流异常

8.1.1 问题描述

  • 在默认情况下,URL 触发限流后会直接返回 Blocked by Sentinel (flow 1imiting)
  • 在实际应用中,大都采用JSON格式的数据,所以如果希望修改触发限流之后的返回结果形式;

8.1.2 实现 UrlBlockHandler 并且重写 blocked() 方法

  • 可以通过自定义限流异常来处理,实现 UrlBlockHandler 并且重写 blocked 方法;
@Service public class CustomurlBlockHandler implements Ur1BlockHandler{
    @Override 
    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpservletResponse, BlockException e)throws IOException{
    httpServletResponse.setHeader("Content-Type","application/json;charset=UTF-8");
    String message="{\"code\":999,\"msg\":\"访问人数过多\"}";
    httpServletResponse.getWriter().write(message);
}
  • 如果触发限流之后,我们希望直接跳转到一个降级页面,可以通过下面这个配置来实现:spring.cloud.sentinel.servlet.block-page-{url}

8.2 URL 资源清洗

8.2.1 问题描述

  • 默认情况下 Sentinel 会把所有的 URL 当作资源来进行流控;
  • 比如一个 URL /clean/{id} 带有一个参数 id,这个参数有很多种属性 {id可取整数},有多少个请求 Sentinel 默认统计多个,而我们期望是一个;

8.2.2 实现 UrICleaner 并重写 clean() 方法

  • 对于 /clean/{id} 这个 URL,我们可以统一归集到 /clean/* 资源下:
@Service 
public class CustomerUrlCleaner implements UrlCleaner{
    @Override 
    public String clean(String originurl){
        if(Stringutils.isEmpty(originur1)){
            return originUrl;
    }
    if(originUr1.startswith("/clean/")){
        return"/clean/*";
    }
    return originUrl;
}

相关文章
|
6天前
|
缓存 负载均衡 JavaScript
探索微服务架构下的API网关模式
【10月更文挑战第37天】在微服务架构的海洋中,API网关犹如一座灯塔,指引着服务的航向。它不仅是客户端请求的集散地,更是后端微服务的守门人。本文将深入探讨API网关的设计哲学、核心功能以及它在微服务生态中扮演的角色,同时通过实际代码示例,揭示如何实现一个高效、可靠的API网关。
|
5天前
|
Cloud Native 安全 数据安全/隐私保护
云原生架构下的微服务治理与挑战####
随着云计算技术的飞速发展,云原生架构以其高效、灵活、可扩展的特性成为现代企业IT架构的首选。本文聚焦于云原生环境下的微服务治理问题,探讨其在促进业务敏捷性的同时所面临的挑战及应对策略。通过分析微服务拆分、服务间通信、故障隔离与恢复等关键环节,本文旨在为读者提供一个关于如何在云原生环境中有效实施微服务治理的全面视角,助力企业在数字化转型的道路上稳健前行。 ####
|
9天前
|
设计模式 Java API
微服务架构演变与架构设计深度解析
【11月更文挑战第14天】在当今的IT行业中,微服务架构已经成为构建大型、复杂系统的重要范式。本文将从微服务架构的背景、业务场景、功能点、底层原理、实战、设计模式等多个方面进行深度解析,并结合京东电商的案例,探讨微服务架构在实际应用中的实施与效果。
48 6
|
9天前
|
设计模式 Java API
微服务架构演变与架构设计深度解析
【11月更文挑战第14天】在当今的IT行业中,微服务架构已经成为构建大型、复杂系统的重要范式。本文将从微服务架构的背景、业务场景、功能点、底层原理、实战、设计模式等多个方面进行深度解析,并结合京东电商的案例,探讨微服务架构在实际应用中的实施与效果。
26 1
|
16天前
|
弹性计算 Kubernetes Cloud Native
云原生架构下的微服务设计原则与实践####
本文深入探讨了在云原生环境中,微服务架构的设计原则、关键技术及实践案例。通过剖析传统单体架构面临的挑战,引出微服务作为解决方案的优势,并详细阐述了微服务设计的几大核心原则:单一职责、独立部署、弹性伸缩和服务自治。文章还介绍了容器化技术、Kubernetes等云原生工具如何助力微服务的高效实施,并通过一个实际项目案例,展示了从服务拆分到持续集成/持续部署(CI/CD)流程的完整实现路径,为读者提供了宝贵的实践经验和启发。 ####
|
5天前
|
Dubbo Java 应用服务中间件
服务架构的演进:从单体到微服务的探索之旅
随着企业业务的不断拓展和复杂度的提升,对软件系统架构的要求也日益严苛。传统的架构模式在应对现代业务场景时逐渐暴露出诸多局限性,于是服务架构开启了持续演变之路。从单体架构的简易便捷,到分布式架构的模块化解耦,再到微服务架构的精细化管理,企业对技术的选择变得至关重要,尤其是 Spring Cloud 和 Dubbo 等微服务技术的对比和应用,直接影响着项目的成败。 本篇文章会从服务架构的演进开始分析,探索从单体项目到微服务项目的演变过程。然后也会对目前常见的微服务技术进行对比,找到目前市面上所常用的技术给大家进行讲解。
16 1
服务架构的演进:从单体到微服务的探索之旅
|
4天前
|
Cloud Native 安全 API
云原生架构下的微服务治理策略与实践####
—透过云原生的棱镜,探索微服务架构下的挑战与应对之道 本文旨在探讨云原生环境下,微服务架构所面临的关键挑战及有效的治理策略。随着云计算技术的深入发展,越来越多的企业选择采用云原生架构来构建和部署其应用程序,以期获得更高的灵活性、可扩展性和效率。然而,微服务架构的复杂性也带来了服务发现、负载均衡、故障恢复等一系列治理难题。本文将深入分析这些问题,并提出一套基于云原生技术栈的微服务治理框架,包括服务网格的应用、API网关的集成、以及动态配置管理等关键方面,旨在为企业实现高效、稳定的微服务架构提供参考路径。 ####
24 5
|
7天前
|
监控 API 微服务
后端技术演进:从单体架构到微服务的转变
随着互联网应用的快速增长和用户需求的不断演化,传统单体架构已难以满足现代软件开发的需求。本文深入探讨了后端技术在面对复杂系统挑战时的演进路径,重点分析了从单体架构向微服务架构转变的过程、原因及优势。通过对比分析,揭示了微服务架构如何提高系统的可扩展性、灵活性和维护效率,同时指出了实施微服务时面临的挑战和最佳实践。
25 7
|
5天前
|
Kubernetes 负载均衡 Cloud Native
云原生架构下的微服务治理策略
随着云原生技术的不断成熟,微服务架构已成为现代应用开发的主流选择。本文探讨了在云原生环境下实施微服务治理的策略和方法,重点分析了服务发现、负载均衡、故障恢复和配置管理等关键技术点,以及如何利用Kubernetes等容器编排工具来优化微服务的部署和管理。文章旨在为开发者提供一套实用的微服务治理框架,帮助其在复杂的云环境中构建高效、可靠的分布式系统。
19 5
|
5天前
|
负载均衡 监控 Cloud Native
云原生架构下的微服务治理策略与实践####
在数字化转型浪潮中,企业纷纷拥抱云计算,而云原生架构作为其核心技术支撑,正引领着一场深刻的技术变革。本文聚焦于云原生环境下微服务架构的治理策略与实践,探讨如何通过精细化的服务管理、动态的流量调度、高效的故障恢复机制以及持续的监控优化,构建弹性、可靠且易于维护的分布式系统。我们将深入剖析微服务治理的核心要素,结合具体案例,揭示其在提升系统稳定性、扩展性和敏捷性方面的关键作用,为读者提供一套切实可行的云原生微服务治理指南。 ####