Skip to content

Sentinel服务哨兵简介

中文文档:https://github.com/alibaba/Sentinel/wiki

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

小结:

Nacos:注册中心,解决服务的注册与发现

Ribbon:客户端的负载均衡器,服务集群的负载均衡

OpenFeign:声明式的HTTP客户端,服务远程调用

nacos-config:配置中心,配置文件的中心化管理

1.0:服务雪崩效应

级联失效、级联故障、cascading failure

image-20201028065801447

解决办法

1:限流

2:仓壁模式 --- 隔离

image-20201028070254466

3:断路器

1.1:Sentinel是什么

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

1.1.1:Sentinel 具有以下特征

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

    image-20201026212724471

  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

1.1.2:Sentinel 分为两个部分:

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

1.2:Sentinel与Hystrix对比

Sentinel是阿里对hystrix的替换

SentinelHystrix
隔离策略信号量隔离(并发线程数)线程池隔离/信号量隔离
熔断降级策略慢调用比例、异常比率、异常数异常比率
实时指标实现滑动窗口滑动窗口(基于 RxJava)
规则配置(rule)支持多种数据源支持多种数据源
扩展性多个扩展点插件的形式
基于注解的支持支持支持
调用链路信息支持同步调用不支持
限流QPS基于 QPS / 并发数,支持基于调用关系的限流不支持
流量整形支持慢启动、匀速器模式不支持
系统负载保护支持不支持
实时监控 API各式各样较为简单
控制台开箱即用,可配置规则、查看秒级监控、机器发现等不完善
常见框架的适配Servlet、Spring Cloud、Dubbo、gRPC 等Servlet、Spring Cloud Netflix

Hystrix已经停更说明

image-20201026211604752

1.3.专业术语

什么是熔断?

A 服务调用B服务的某个功能,由于网络不稳定问题、B服务卡机等 导致功能时间超长,如果这样的情况次数太多,我们就可以直接将B断路了(A不在请求B接口) 凡是调用B的直接返回降级数据 ,不必等待B的超长执行。这样B的故障问题 就不会级联影响到A 。 某些被调用的服务故障,触发系统保护规则 一种被动保护

什么是降级?

整个网站都处于流量高峰期,服务器压力剧增,根据当前业务情况及流量,对一些服务和页面进行有策略的降级(停止正常服务,所有的调用直接返回降级数据) 以此缓解服务器资源的压力,以保证核心业务的正常运行,同时也保持了客户或大部分客户得到正确的响应。由于服务器资源紧张,主动去关闭某些服务,降级某些服务 一种 主动保护

异同:

​ 相同点 :1.为了保证集群大部分服务的可用性和可靠性,防止崩溃 牺牲小我。

​ 2.用户最终都是体验到某个功能不可用

​ 不同点:1.熔断是被调用方故障 触发的系统的主动规则

​ 2.降级是基于全局考虑,停止一些正常服务,释放资源。

什么是限流?

对打入服务的请求 流量进行控制 使服务能够承担不超过自己能力的流量压力 对入口流量进行控制

1.4使用sentinel的三个步骤

1.定义资源 ------ 定义哪些资源需要保护

2.定义规则

3.检验规则是否生效

先把可能需要保护的资源定义好,之后再配置规则,只要有了资源,我们就可以在任何时候灵活的定义各种流量控制规则,在编码的时候只需要考虑这个代码是否需要保护,如果需要保护 ,就将之定义为一个资源。

1.4.1 定义资源的方式

参考官网:如何使用 · alibaba/Sentinel Wiki · GitHub

方式1 主流框架的默认适配

为了减少开发的复杂程度,我们对大部分的主流框架,例如 Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor 等都做了适配。您只需要引入对应的依赖即可方便地整合 Sentinel。可以参见: 主流框架的适配。 和这些框架默认适配, 默认所有的请求都是要受保护的资源。

方式2 抛出异常的方式定义资源

SphU 包含了 try-catch 风格的 API。用这种方式,当资源发生了限流之后会抛出 BlockException。这个时候可以捕捉异常,进行限流之后的逻辑处理。示例代码如下:

java
// 1.5.0 版本开始可以利用 try-with-resources 特性(使用有限制)
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
try (Entry entry = SphU.entry("resourceName")) {
  // 被保护的业务逻辑
  // do something here...
} catch (BlockException ex) {
  // 资源访问阻止,被限流或被降级
  // 在此处进行相应的处理操作
}

特别地,若 entry 的时候传入了热点参数,那么 exit 的时候也一定要带上对应的参数(exit(count, args)),否则可能会有统计错误。这个时候不能使用 try-with-resources 的方式。另外通过 Tracer.trace(ex) 来统计异常信息时,由于 try-with-resources 语法中 catch 调用顺序的问题,会导致无法正确统计异常数,因此统计异常信息时也不能在 try-with-resources 的 catch 块中调用 Tracer.trace(ex)

手动 exit 示例:

java
Entry entry = null;
// 务必保证 finally 会被执行
try {
  // 资源名可使用任意有业务语义的字符串,注意数目不能太多(超过 1K),超出几千请作为参数传入而不要直接作为资源名
  // EntryType 代表流量类型(inbound/outbound),其中系统规则只对 IN 类型的埋点生效
  entry = SphU.entry("自定义资源名");
  // 被保护的业务逻辑
  // do something...
} catch (BlockException ex) {
  // 资源访问阻止,被限流或被降级
  // 进行相应的处理操作
} catch (Exception ex) {
  // 若需要配置降级规则,需要通过这种方式记录业务异常
  Tracer.traceEntry(ex, entry);
} finally {
  // 务必保证 exit,务必保证每个 entry 与 exit 配对
  if (entry != null) {
    entry.exit();
  }
}

热点参数埋点示例:

java
Entry entry = null;
try {
    // 若需要配置例外项,则传入的参数只支持基本类型。
    // EntryType 代表流量类型,其中系统规则只对 IN 类型的埋点生效
    // count 大多数情况都填 1,代表统计为一次调用。
    entry = SphU.entry(resourceName, EntryType.IN, 1, paramA, paramB);
    // Your logic here.
} catch (BlockException ex) {
    // Handle request rejection.
} finally {
    // 注意:exit 的时候也一定要带上对应的参数,否则可能会有统计错误。
    if (entry != null) {
        entry.exit(1, paramA, paramB);
    }
}

SphU.entry() 的参数描述:

image-20210619123144923

方式3 返回布尔值方式定义资源

image-20210619123154493

方式4 注解方式定义资源

image-20210619123203480

方式5 异步调用支持

.........TODO

常用的是 方式 1 2 4 ,特别是方式2 可以任意指定受保护的资源

1.4.2 规则的种类

流量控制规则 flowrule

熔断降级规则 degraderule

系统保护规则

访问控制规则

热点规则

1.5:Sentinel架构

image-20201028011759647

properties
#接口概览
http://127.0.0.1:8720/api
#获取资源的metrics(指标)信息
http://127.0.0.1:8720/cnode?id=/jifen/{jifenId}
#获取流控规则接口
http://127.0.0.1:8720/getRules?type=flow
#设置规则接口
http://127.0.0.1:8720/setRules

1.6:微服务整合Sentinel

查看主流框架的适配 https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6%E7%9A%84%E9%80%82%E9%85%8D

找到SpringBoot/SpringCloud

https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel

第一步:添加依赖

xml
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

查看sentinel 的依赖版本,咱们当前依赖 是 1.8.0版本 所以下载 1.8.0 版本的dashboard

image-20210818151658886

第二步:配置

properties
#默认是8719,如果8719被占用 +1,一直到没有被占用
spring:
  cloud:
    sentinel:
      transport:
        port: 8719     # 当前服务 和 sentinel 控制台 通讯的端口
        dashboard: localhost:8777     # 控制台的地址
properties
#开启饥额加载,当项目启动立即完成初始化,默认是懒加载
spring.cloud.sentinel.eager=true

image-20210818152033383

1.7:Sentinel控制台安装

https://github.com/alibaba/Sentinel/releases找到咱们依赖对应的版本 下载

注意:启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本。

功能

1:实时观看每个资源的访问情况(qps)

2:通过控制台,设置流控规则(qps、线程数)、还可以设置熔断降级的规则(慢调用比例、异常比例、异常数)

shell
java -jar sentinel-dashboard-1.8.0.jar --server.port=8777

访问 localhost:8777

image-20201228201026453

默认 用户 密码 sentinel

每个服务 都需要配置 连接sentinel 控制台的信息

打开控制台 ,看到如下界面,在 簇点链路中 可以看到 咱们定义的资源,当然 咱们暂时 没有 自己定义资源,在这看到的是 sentinel 和主流框架 适配, 默认的资源

image-20230613164643708

1.8 Sentinel服务流控

Sentinel通过流量控制(flow control)以及熔断降级来保护系统资源

1.8.1 QPS超过阈值直接失败

流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

txt
QPS:Queries-per-second,每秒资源被访问的次数
当被访问资源的QPS超过阈值,直接进行限流(限制访问),从而保护资源,

触发条件:/order/test1当qps>1时,会触发流控

image-20211224105849402

效果是 访问 /order/test1 超出qps的阈值 就会被限流 默认返回

image-20211224105946091

线程数

image-20211224110026290

服务端允许一个线程访问,超出一个线程 会被限流

QPS超过阈值关联失败

image-20211224110140545

访问 /order/test2 超出 1 qps ,则 /order/test1 会被限流, 注意 不是限流 /order/test2

线程超过阈值关联失败

qps超过阈值链路失败

image-20210818170718455

properties
spring:
  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8777
      eager: true
      web-context-unify: false        # 开启链路失败的效果
feign:
  sentinel:
    enabled: true    # 开启链路失败的效果
java
//OrderController 
@RequestMapping("/test3")
    public String test3(String name){
        // 演示 流控模式  链路
        String result = orderService.test3(name);

        return "cloud-order-test3:"+result;
    }

    @RequestMapping("/test4")
    public String test4(String name){

        String result = orderService.test3(name);
        return "cloud-order-test4:"+result;
    }

//OrderServiceImpl
@Override
    public String test3(String name) {

        if(name!=null&&name.equals("stock")){
            return stockApi.testStock();
        }else if(name!=null&&name.equals("account")){
            return accountApi.testAccount();
        }else if(name!=null&&name.equals("ex")){
            int i = 5/0;
            System.out.println("ex");
        }
        return "order-service-test3";
    }

这里 咱们得场景是 从 订单服务的 test3 接口 和 test4 接口 作为 入口 都去调用 库存服务的接口, 在 控制台 需要在 订单服务中 针对 远程的资源 指定 链路流控规则, 在 库存服务指定 没有效果

image-20230613172221448

image-20230613172353324

触发条件:GET:http://cloud-stock/stock/testStock 有两个入口,分别为/order/test3和/order/test4

当/order/test3访问qps>1 那么/order/test3就不能访问受保护资源,其他入口照常访问

线程超过阈值链路失败

流控效果:warm up

Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考 流量控制 - Warm Up 文档,具体的例子可以参见 WarmUpFlowDemo

image-20230613173549012

预热 为什么要预热? 因为服务刚启动,或者 不稳定的时候 可能有别的行为,比如初始化工作要做,会占用系统资源,导致无法一开始就满足大的并发,但是系统启动一段时间之后,稳定之后,就有足够多的资源来承担高并发高负载 ,所以 有预热或者叫冷启动这种 流控效果。

不好演示 ,了解一下

流控效果:匀速排队

匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 流量控制 - 匀速器模式,具体的例子可以参见 PaceFlowDemo

该方式的作用如下图所示:

image-20230613173619346

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

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

之前的流控效果都是超出阈值了 就不处理了,不让进来,而某些场景 每个请求都必须要处理,就算超出系统负载也得处理,此时 就需要用到 匀速排队这种流控效果了,超出了我的负载,我就时间换空间, 匀速排队处理。但是 这个也是有上限的 ,比如

image-20211225202711743

比如 每秒2000 个请求,除以 超时时间 10秒 ,每秒200 个请求,没有超出阈值1000 则 可以正常匀速处理,如果每秒100000个请求,除以10 超时时间,则 是10000 ,10000大于阈值1000 ,那多出来的请求 还是会丢

1.9Sentinel服务熔断降级

慢调用比例

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

  • 断路器的工作流程:

    一旦熔断,断路器的状态是Open(所有的请求都不能进来)

    当熔断时长结束,断路器的状态是half-Open(可以允许一个请求进来)

    如果接下来的请求正常,断路器的状态是close(资源就自恢复)

    如果接下来的请求不正常,断路器的状态是open

触发条件

1:1s内最小请求数必须大于5

2:man请求/总请求 > 0.6

image-20210819105935516

异常比例

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

异常数

2.0Sentinel-热点key规则

注意:默认的请求路径资源 不生效 需要手动指定 资源 比如 通过 @SentinelResource 注解指定

触发条件

1:必须有一个参数,参数的值可以任意 例如:http://localhost:9002/order/test7?name=jack

2:接口访问qps>1

image-20210819140928599

触发条件

1:第一个参数的值如果为jack,那么qps阈值为1

2:第一个参数的值如果为rose,那么qps阈值为5

3:第一个参数的值如果为其他,那么qps阈值为1

image-20210819141742327

注意事项:

image-20210330163338800

2.1 授权规则

image-20211225213547492

把cloud-order设置为黑名单,就不能调用/stock/testStock这个资源了,注意 这里的 cloud-order 指的不是服务名 而是请求头

演示案例:

cloud-order 远程调用cloud-stock 服务,

在 cloud-order 服务配置 下面的代码 利用feign 的拦截器 统一设置请求头

java
package com.qf.order.feign;

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;

/**
 * @ClassName : CustomerRequestInterceptor
 * @Author : glls
 * @Date: 2021/12/25 21:20
 * @Description :
 */
@Component
public class CustomerRequestInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        //统⼀设置请求头
        requestTemplate.header("source","cloud-order");   //order 服务 发送feign 远程调用携带这个头
    }
}

在 cloud-stock服务 获取请求头 ,这里 用到sentinel 提供的接口 RequestOriginParser,这里获取到的 头信息 就是上文 白名单 或者黑名单的内容

java
package com.qf.stock.config;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @ClassName : CustomerRequestOriginParser
 * @Author : glls
 * @Date: 2021/12/25 21:19
 * @Description :
 */
@Component
public class CustomerRequestOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
        System.out.println("come in");
        //获取请求头的source
        String source1 = request.getHeader("source");  //获取调用方发过来的头 ,然后在授权规则 配置 
        System.out.println(source1);
        return source1;
    }
}

测试 把cloud-order 设置为黑名单,发现 无法调用cloud-stock 服务了,注意 这个授权规则 需要在 cloud-stock 设置

image-20211225214327944

image-20211225214401978

image-20211225214443397

其实 在流控那里配置的 针对来源 也是这样配置的通过在调用方 和 被调用方配置两个 配置类,指定 来源的内容。不用在演示了吧,你懂得。

2.2 系统规则

  • Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0)。
  • RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

2.3 定义资源的方式

2.3.1 适配主流框架

controller 层的接口 会被当做 默认资源,

image-20230614144744728

跟 主流框架 默认适配的资源 全局异常处理器 无法处理,又想返回自定义的响应 ,可以使用 BlockExceptionHandler

java
package com.glls.order.exception;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @date 2023/6/14
 * @desc
 */
@Service
public class CustomerBlockHandler implements BlockExceptionHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws Exception {
        String msg = null;
        if (ex instanceof FlowException) {
            msg = "限流了";
        } else if (ex instanceof DegradeException) {
            msg = "降级了";
        } else if (ex instanceof ParamFlowException) {
            msg = "热点参数限流";
        } else if (ex instanceof SystemBlockException) {
            msg = "系统规则(负载/...不满足要求)";
        } else if (ex instanceof AuthorityException) {
            msg = "授权规则不通过";
        }
        // http状态码
        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Type", "application/json;charset=utf-8");
        response.setContentType("application/json;charset=utf-8");

        response.getWriter().write("{\"error\":\"自定义流控响应\"}");
    }
}

2.3.2 @SentinelResource 注解

参考资料

咱们可以 借助 这个注解的 属性 blockHandler/blockHandlerClass ,fallback/fallbackClass

image-20230614144038524

资源定义 流控自定义异常处理 熔断降级自定义处理

以抛出异常的方式定义资源

java
 @Override
   
    public String test3(String testName) {

        // 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
        try (Entry entry = SphU.entry("resourceName")) {
            if (testName != null && testName.equals("stock")) {
                return stockFeignApi.testStock();
            } else if (testName != null && testName.equals("account")) {
                return accountFeignApi.testAccount();
            } else if (testName != null && testName.equals("ex")) {
                int i = 5 / 0;

            } else {
                return "other";
            }
        } catch (BlockException ex) {
            // 资源访问阻止,被限流或被降级
            // 在此处进行相应的处理操作
            log.error("资源触发规则");
        }


        return "other";
    }

以注解的形式定义资源

java
 @Override
    @SentinelResource(value = "test3resource",fallback="test3Fallback",blockHandler = "test3BlockHandler",blockHandlerClass = CustomBlockHandler.class)
    public String test3(String testName) {

       
            if (testName != null && testName.equals("stock")) {
                return stockFeignApi.testStock();
            } else if (testName != null && testName.equals("account")) {
                return accountFeignApi.testAccount();
            } else if (testName != null && testName.equals("ex")) {
                int i = 5 / 0;

            } else {
                return "other";
            }
      
        return "other";
    }

注意 资源触发规则 则 走 blockHandler , 资源出现 其他异常 ,则走 fallback

2.3.3 捕获异常的方式

java
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
        try (Entry entry = SphU.entry("resourceName")) {
            if (testName != null && testName.equals("stock")) {
                return stockApi.testStock();
            } else if (testName != null && testName.equals("account")) {
                return accountApi.testAccount();
            } else if (testName != null && testName.equals("ex")) {
                int i = 5 / 0;

            } else {
                return "other";
            }
        } catch (BlockException ex) {
            // 资源访问阻止,被限流或被降级
            // 在此处进行相应的处理操作
            log.error("资源触发规则");
           
        }

2.4 规则持久化

原始模式

如果不做任何修改,Dashboard 的推送规则⽅式是通过 API 将规则推送⾄客户端并直接更新 到内存中: 这种做法的好处是简单,⽆依赖;坏处是应⽤重启规则就会消失,仅⽤于简单测试,不能⽤ 于⽣产环境。

image-20211225220836096

Pull模式

FileRefreshableDataSource 定时从指定⽂件中读取规则JSON⽂件【图中的本地⽂件】,如 果发现⽂件发⽣变化,就更新规则缓存。 FileWritableDataSource 接收控制台规则推送,并根据配置,修改规则JSON⽂件【图中的本 地⽂件】。

image-20211225220855034

为什么说是 pull 模式,拉模式 ,因为 最终 生效的规则 是在内存中配置的,咱们需要把持久化到本地文件的规则 拉倒内存中去,所以称为 拉模式 ,咱们可以在 dashboard 和 本地文件 修改规则,然后在内存中生效,这里的拉 主要就是通过定时任务 拉去本地json 文件中的规则。

实现步骤:

在 微服务端 添加依赖:

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

核心代码:

Push模式 推荐

动态规则扩展 · alibaba/Sentinel Wiki · GitHub

修改 sentinel-dashboard 源码 实现 dashboard 推送数据到 nacos

fans-sentinel-master: 在Alibaba Sentinel-master基础上添加nacos数据源,同时sentinel操作面板更新的数据同步到nacos的功能 (gitee.com)

注意 push 模式的 流控规则 持久化 可能会推送不到 nacos 配置中心 ,原因可能是 浏览器 缓存导致的 可以尝试清除缓存 再进行流控规则制定

生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了:

Remote push rules to config center

9.3.1:控制台改造

改动 sentinel 的源码

懒人包

image-20210824151803313

注意namespace 需要提前创建好

9.3.2:微服务端

xml
<!-- sentinel -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- sentinel的nacos持久化 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

9.3.3:添加配置

properties
spring:
  cloud:
    sentinel:
      transport:
        port: 8721
        dashboard: localhost:8777
      eager: true #饥饿加载
      web-context-unify: false
      datasource:
        flow:
          nacos:
            server-addr: ${nacos.server-addr}
            username: ${nacos.username}
            password: ${nacos.password}
            namespace: ${nacos.namespace}
            groupId: SENTINEL_GROUP
            dataId: ${spring.application.name}-flow-rules
            rule-type: flow
        degrade:
          nacos:
            server-addr: ${nacos.server-addr}
            username: ${nacos.username}
            password: ${nacos.password}
            namespace: ${nacos.namespace}
            groupId: SENTINEL_GROUP
            dataId: ${spring.application.name}-degrade-rules
            rule-type: degrade
        param-flow:
          nacos:
            server-addr: ${nacos.server-addr}
            username: ${nacos.username}
            password: ${nacos.password}
            namespace: ${nacos.namespace}
            groupId: SENTINEL_GROUP
            dataId: ${spring.application.name}-param-rules
            rule-type: param-flow
        system:
          nacos:
            server-addr: ${nacos.server-addr}
            username: ${nacos.username}
            password: ${nacos.password}
            namespace: ${nacos.namespace}
            groupId: SENTINEL_GROUP
            dataId: ${spring.application.name}-system-rules
            rule-type: system
        authority:
          nacos:
            server-addr: ${nacos.server-addr}
            username: ${nacos.username}
            password: ${nacos.password}
            namespace: ${nacos.namespace}
            groupId: SENTINEL_GROUP
            dataId: ${spring.application.name}-authority-rules
            rule-type: authority
nacos:
  server-addr: localhost:8848
  username: nacos
  password: nacos
  namespace: sentinel

切记 一定要把 浏览器的缓存清掉

2.5 sentinel 对网关进行保护

1.添加依赖

shell
  <!--整合  sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
        </dependency>

2.修改配置文件

image-20211227152201125

  1. 在网关服务添加配置类

    java
    package com.qf.gateway.config;
    
    import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
    import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
    import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
    import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
    import org.springframework.beans.factory.ObjectProvider;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.Order;
    import org.springframework.http.codec.ServerCodecConfigurer;
    import org.springframework.web.reactive.function.server.ServerResponse;
    import org.springframework.web.reactive.result.view.ViewResolver;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    import javax.annotation.PostConstruct;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * @ClassName : GatewayConfiguration
     * @Author : glls
     * @Date: 2021/12/27 14:46
     * @Description :
     */
    @Configuration
    public class GatewayConfiguration {
    
        private final List<ViewResolver> viewResolvers;
        private final ServerCodecConfigurer serverCodecConfigurer;
    
        public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                    ServerCodecConfigurer serverCodecConfigurer) {
            this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
            this.serverCodecConfigurer = serverCodecConfigurer;
        }
    
        @Bean
        @Order(Ordered.HIGHEST_PRECEDENCE)
        public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
            // Register the block exception handler for Spring Cloud Gateway.
            return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
        }
    
        @Bean
        @Order(Ordered.HIGHEST_PRECEDENCE)
        public GlobalFilter sentinelGatewayFilter() {
            return new SentinelGatewayFilter();
        }
    
    
        @PostConstruct
        public void init(){
            // 自定义  流控后的 降级数据
            GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
                // 网关限流了   就会调用这个  回调方法
    
                // Mono  Flux  spring5 特性 响应式编程   后面可以研究下
                @Override
                public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
    
                    Mono<ServerResponse> body = ServerResponse.ok().body(Mono.just("{\"error\":\"gateway流控返回\"}"), String.class);
                    return body;
                }
            });
        }
    
    }

4.网关流控控制台

image-20211227152341943

java
-Dcsp.sentinel.app.type=1

5.演示 网关 流控效果