SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵

文章目录

  • 一、Sentinel是什么?
    • 1.1 Sentinel 具有 以下特性
  • 二、Sentinel 使用
    • 2.1 Sentinel 安装。
    • 2.2 创建 `sgg-alibaba-sentinel12001` module
    • 2.3 自定义异常
    • 2.4 熔断降级
    • 2.5 热点参数限流
    • 2.6 系统自适应限流
    • 2.7 黑白名单控制
    • 2.8 注解支持
    • 2.9 规则管理以及推送
      • 1.1 使用 Nacos 作为数据源
    • 2.11 整合Feign
  • 总结: 感谢B站尚硅谷的老师们


一、Sentinel是什么?

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

1.1 Sentinel 具有 以下特性

  • 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一促流量的核心场景,比如秒杀,消息削峰填谷,集群流量控设置,实时熔断下游不可用应用等。
  • 完备的实时监控: Sentinel 同时提供了实时的监控服务,您可以在控制台中看到接入应用的单台机器秒级数据,甚至500台以下规模的集群汇总运行情况
  • 广泛的开源生态: Sentinel 提供了开箱即用的与其他开源框架的整合模块,例如与 SpringCloud,Dubbo,gRPC 的整合
  • 完善的 SPI 扩展点: Sentinel 提供简单易用,完善的SPI 扩展接口,可以通过实现扩展接口来快速指定逻辑,例如定制管理规则,适配动态数据源等
    SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第1张图片
    以上来自官方文档

二、Sentinel 使用

2.1 Sentinel 安装。

直接github 下载
Sentinel 下载地址
我下载的 1.7.0 版本的。 下载下来直接解压。是一个 jar 包。
在这里插入图片描述
通过 cmd 直接运行。
java -jar sentinel-dashboard-1.7.0.jar

Sentinel 默认运行在 8080 端口上。
在这里插入图片描述
直接访问 http://localhost:8080 通过sentinel / sentinel 登录
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第2张图片
因为 Sentinel 控制台是基于 Spring Boot 实现,所以我们可以通过启动时的命令行参数,自定义配置。

  • –server.port:自定义服务器端口。默认为 8080 端口。
  • –auth.username 和 --auth.password:自定义账号和密码。默认为「sentinel / sentinel」。
  • –logging.file:自定义日志文件。默认为 ${user.home}/logs/csp/sentinel-dashboard.log。

2.2 创建 sgg-alibaba-sentinel12001 module

引入依赖

<dependency>
	 <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>

<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
dependency>

创建配置文件 , 因为我们依旧使用nacos 作为注册中心。 但是为了方便。我把配置文件直接放在 yml 文件中

server:
  port: 12001
spring:
  application:
    name: sgg-alibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        service: ${spring.application.name}
      config:
        group: DEFAULT_GROUP
        namespace:
        file-extension: yaml
        name: ${spring.application.name}
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080 # 配置 Sentinel DashBoard 的地址
        port: 8719 # 默认为  8719 端口。如果被占用 则自动回复从 8719 开启扫描。 一直到未找到没有被占用的端口
      enable: true # 是否开启 默认为true 开启
      eager: true # 是否懒加载。 意思就是如果没有发生过访问。 我们的 Sentinel 控制台中是没有记录的。
      filter:
        url-patterns: /** # 拦截请求的地址 默认为 /* 
management:
  endpoints:
    web:
      exposure:
        include: "*"

我只是吧配置贴了出来。 希望还是正常吧配置放入到 nacos 中,练练手
enable :配置是否开启Sentinel , 默认为 true 开启。 、
eageer : 配置是否懒加载。 默认为true 开启。 默认情况下。Sentinel 是延迟初始化, 在首次使用 Sentinel 才进行初始化, 通过设置为 true 时。在项目启动时就会将 Sentinel 直接初始化,完成 Sentinel 控制台进行注册。
transport.dashboard : 配置 Sentinel 控制台地址
filter.url-patterns : 配置 拦截请求的地址 默认为 /**
在 Sentinel 的子项目中, sentinel-spring-webmvc-adapter 中, 对SpringMVC 进行适配。 通过 SentinelWebInterceptor 拦截器, 实现对 SpringMVC的拦截请求。 使用 Sentinel 进行保护, 通过 filter.url-patterns 配置项。 可以定义拦截器的拦截请求地址

创建我们的controller 和 启动类
SentinelController.javaAliababSentinel12001App.java

@RestController
@Slf4j
@RequestMapping(value = "sentinel")
public class SentinelController {
    
    @GetMapping(value = "/testA")
    public String testA() {
     
        return "testA";
    }

    @GetMapping(value = "/testB")
    public String testB() {
	    Thread.sleep(800);
        return "testB";
    }
}
@SpringBootApplication
@EnableDiscoveryClient
public class AliababSentinel12001App {
    public static void main(String[] args) {
        SpringApplication.run(AliababSentinel12001App.class, args);
    }
}

启动项目 。 访问我们的接口 http://localhost:12001/sentinel/testA
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第3张图片
当我们疯狂点击 testA 接口的时候。这里就会出现我们的机器的流量监控等等。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第4张图片
找到我们的接口。 点击流控。 通过 qps 限制我们的流量 。

SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第5张图片
QPS: 就是每秒我们接口的请求数。 我们这里设置单机阈值为 1 , 也就是说我们每秒只接受 1 个请求。 设置完之后,点击新增 。
然后再疯狂访问我们的接口。 你就会发现他就会出现访问失败的情况
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第6张图片

2.3 自定义异常

SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第7张图片
在SentinelWebInterceptor 拦截器中, 当请求满足配置的 Sentinel block 条件时。 Sentinel 会抛出 BlockException 异常。并通过 定义 BlockExceptionHandler 接口的实现类。可以实现对BlockException 异常处理

默认情况下, BlockException 有一个默认的 DefaultBlockExceptionHandler 类。 返回 Block 字符串提示。 ·
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第8张图片
返回的就是我们访问接口时返回的字符串 , 所以我们只需要重写 BlockExceptionHandler 直接抛出异常, 交给 SpringMVC的全局处理器处理噻

@Component
public class CustomBlockExceptionHandler implements BlockExceptionHandler {

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
        throw e;
    }
}

配置 全局处理器

@Component
@RestControllerAdvice(basePackages = "cn.fllday.controller")
public class GlobalExceptionAdviceHandler {

    @ExceptionHandler(value = BlockException.class)
    public JSONObject blockExceptionHandler(BlockException blockException){
        JSONObject error = new JSONObject();
        error.put("code", 2048);
        error.put("msg", "服务器繁忙,请稍后再试");
        error.put("ex", blockException.getClass().getSimpleName());
        return error;
    }
}

重启服务。 访问 http://localhost:12001/sentinel/testA
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第9张图片
可以看到这次返回的信息就非常友好了。
测试一下基于线程数的限流。 使用 testB 接口。 testB 使用了沉睡。 大概 800 ms。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第10张图片
我们开两个窗口同同时访问 testB 接口。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第11张图片
同时只允许一个线程执行。 有点不太好测试 嘻嘻

2.4 熔断降级

Sentinel 和 Hystrix 的原则是一致的。 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间过长, 异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联故障。

Sentinel 和 Hystrix 采用了完全不一致的方法

Hystrix 通过 线程池隔离 的方式,来对依赖进行了隔离。这样的好处是 资源和资源之间做到了最彻底的隔离。缺点是增加了线程切换的成本。 还需要预先给各个资源分配线程池的大小

Sentinel 对这个问题采用了两种手段
1、 通过并发线程数进行控制
和资源池隔离的方式不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其他资源的影响,这样不但没有线程切换的损耗,也不需要预先分配线程池的大小,当某个资源出现不稳定的情况下,例如 响应时间边长,对资源的直接影响就会造成线程数的逐步堆积,当线程数在特定资源上堆积到一定的数量之后,对该线程的心情求就被拒绝,堆积的线程完成任务后,才会继续接受新的请求
2、通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源, 当以来的资源出现响应时间过长之后, 所有对该资源的访问都会被直接拒绝,直到过了指定时间窗口之后才会重新恢复

我们还是使用刚刚创建的服务 ,在我们的Sentinel 控制台中找到 降级按钮 ,对我们的 /testB 接口
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第12张图片
点击降级按钮
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第13张图片
平均响应时间:DEGRADE_GRADE_RT :

当一秒内持续进入 5 个请求,对应时刻的平均响应时间(秒级) 均超过阈值(count, 以 ms 为单位), 那么在接下来的时间窗口(DegradeRule 中的timeWindow , 以 s 为单位)\之内 , 这个方法的调用都会自动熔断。 抛出异常 : DegradeException

注意: Sentinel 默认的RT上限是 4900ms。 超出此阈值都会算作 4900 ms . 若需要变更此上限,可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置

异常比例: DEGRADE_GRADE_EXCEPTION_RATIO : 当资源的每秒请求量 >= 5, 并且每秒的异常总量占数总通过量的比值超过 阈值 (DegradeRule 中的 timeWindow) 以 s 为单位之内。 对这个方法都会自动的返回。 异常比率的阈值范围是 [0.0, 1.0] 代表 0% - 100%

异常数: DEGRADE_GRADE_EXCEPTION_COUNT
当资源近 1 分钟的异常数目超过阈值之后会进行熔断,注意 由于统计时间窗口十分钟级别的。 若是 timeWindow 小于 60s。 则结束熔断状态后仍可能再进入熔断状态。

点击新增之后。完成降级规则的添加。
在这里插入图片描述
这边配置的是 阈值是 500ms , 时间窗口是 10 s。 也就是说 我们的平均响应如果超出了 500ms。 那么在10s 内都不能访问了。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第14张图片
一直访问一直访问。 每次都超出了 500ms。 等 过了 10秒钟之后
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第15张图片
我觉得我又行了

2.5 热点参数限流

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

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

热点参数限流, 会统计传入参数的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第16张图片
Sentinel 利用LRU策略统计最近最常访问的热点参数, 结合令牌桶算法来进行参数级别的流控,热点参数限流支持集群模式

简单测试一下。 在我们的controller 中添加方法

    @GetMapping(value = "/testC")
    @SentinelResource(value = "getTestC")
    public String testC(@RequestParam(value = "p1", required = false) String p1,
                        @RequestParam(value = "p2", required = false) String p2){
        return "testC : " + p1 + p2;
    }

在方法上,我们添加了 @SentinelResource 注解, 自定义了 getTestC 资源。 用于定义资源,并提供可选的异常处理和 fallback 配置项
重新启动
在这里插入图片描述
当我们访问过一次 testC 接口之后,就会在我们的簇点链路里面找到我们的接口,点击热点。

SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第17张图片
新增以上配置 ,索引0, 统计时长为 60 秒,请求最大次数是 10. 点击新增之后跳转到以下页面
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第18张图片
点击编辑
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第19张图片
这边配置的意思是。 我们的 testC 接口当携带第一个参数的时候。 60秒内只允许访问 10次,但是如果第一个参数的值为 11 的 时候,则只允许访问 1 次。 点击保存之后简单测试一下

SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第20张图片
访问第 11 次的时候就会出现,服务器繁忙。 可以看到抛出的异常时 ParamFlowException 。 我们换个参数 p2 继续访问
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第21张图片
这边是不会限制访问的。 当使用 p1 = 11 的时候,仅仅只访问了一次。 第二次就已经报错了。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第22张图片
可以看到热点参数限流和名字一样。是针对每一个参数限流。

2.6 系统自适应限流

Sentinel 同时提供系统维度的自适应保护能力, 防止雪崩,是系统防护中最重要的一环,当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应,在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其他的机器上,如果这个时候其他的机器也处于一个边缘状态的情况下,这个增加的流量就会导致这台机器的崩溃。最后导致整个集群不可用。

针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

Sentinel 一共有5种系统规则

1、 Load 自适应
系统的 load1 作为启发指标。 进行自适应系统保护, 当系统 load1 超过设定的启发值, 且系统的并发线程超过估算的系统容量时才会触发系统保护, 系统容量由 系统的 maxQps * minRt 估算得出,设定参考值一般是 0CPU cores * 2.5

2、CPU usage 1.5.0+ 版本
当系统CPU使用率超过阈值时,触发保护系统。 取值 0.0 - 1.0

3、平均RT
当单台机器上所有入口流量的平均RT达到阈值时即触发系统保护。单位是毫秒

4、并发线程数
当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护

5、入口QPS
当单台机器上所有入口流量的QPS达到阈值即触发系统保护

选择系统规则
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第23张图片
我们配置一个系统规则。根据cpu 配置
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第24张图片

设置为 百分之 1, 比较好实现。 然后访我们的接口
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第25张图片
记得重启一下。清空其他的流控规则。 可以看到,每次请求都是 失败。

2.7 黑白名单控制

我们想要使用 Sentinel 黑白名单控制的时候,所以需要获得请求的调用方, RequestOriginParser 没有提供默认的实现。所以我们可以自定义 CustomRequestOriginParser 实现类。 解析请求头 s-user
创建 CustomRequestOriginParser 实现类。

@Component
public class CustomRequestOriginParser implements RequestOriginParser {
    private static final String DEFAULT_S_USER = "default";
    @Override
    public String parseOrigin(HttpServletRequest request) {
        String origin = request.getHeader("s-user");
        if (StringUtils.isEmpty(origin)) {
            return DEFAULT_S_USER;
        }
        return origin;
    }
}

获取 x-user 的值。 作为请求来源。注意: Sentinel 黑白名单的控制,一般是服务和服务之间的调用。 例如说 配置订单服务允许用户服务调用
判断未获得请求来源的时候,设置值为 default , 原因是 Sentinel 提供的 AuthorityRuleChecker 在进行黑白名的控制时, 如果请求来源为空。 就直接通过了

简单测试, 重启服务, 重启之后,访问一下 http://localhost:12001/sentinel/testA 保证 资源的初始化。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第26张图片
选择授权规则。点击新增授权规则

SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第27张图片
点击保存之后。我们用浏览器访问接口

SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第28张图片
可以看到返回的异常是 AuthorityException ,我们使用 PostMan 访问接口。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第29张图片

2.8 注解支持

在之前的的模块中,有使用到一个注解。使用 @SentinelResource 注解声明自定义资源 ,通过Spring AOP 注解该注解的方法,自动调用 Sentinel 客户端 API
@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:
value : 资源名称, 必须向。 不能为空
entryType : entry 类型, 可选项 (默认 EntryType.OUT
bloickHandler / blockHandlerClass : blockHandler 对应处理 BlockException 的函数名称。可选项, blockHandler 函数访问范围需要是 public, 返回类型需要与原方法相匹配, 参数类型需要和原方法相匹配并且最后增加一个额外的属性, 类型为 BlockExceptionblockHandler 函数默认需要和原方法在同一个类中。如果希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的对象。注意对应的函数必须为static 否则无法解析。

fallback : Fallback 函数名称, 可选项用于在抛出异常的时候,提供 fallback 处理。 fallback 函数可以针对所有的类型异常。 除了 exceptionsToIgnore 里面派出的异常类型进行处理。

  1. 返回值类型必须与原函数返回值类型一致
  2. 方法参数列表需要和原函数一致,或者可以额外多一个 throwable 类型的异常参数
  3. fallback 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类 class 对象。 注意对应的函数必须为 static 函数,否则无法解析
    defaultFallback : 默认的fallback 函数名称。 可选项,通常用于通用的fallback逻辑, 默认 fallback 函数可以针对所有类型的异常进行处理, 若同时配置了 fallback 和 defaultFallback, 则只有 fallback 会生效, defaultFallback 函数签名要求。
  4. 返回值类型必须与原函数返回值类型一致
  5. 方法参数列表需要和原函数一致,或者可以额外多一个 throwable 类型的异常参数
  6. fallback 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类 class 对象。 注意对应的函数必须为 static 函数,否则无法解析
    exceptionToIngore : 用于指定哪些异常被排除掉。 不会计入异常统计中, 也不会进入 fallback 逻辑中, 而是原样抛出

搭建一个基于 @SentinelResource 的demo 示例, 还是在我们的 12001 服务上面修改

 /**
     * blockHandler 只会处理 {@link com.alibaba.csp.sentinel.slots.block.BlockException} 的异常
     * fallback 用于抛出异常的时候提供 fallback 的处理逻辑。 可以针对所有类型的异常。 除了 exceptionToIgnore 忽略的异常
     * @param id
     * @return
     */
    @GetMapping(value = "/annotationsDemo")
    @SentinelResource(value = "getAnnotationsDemo", blockHandler = "blockHandler", fallback = "fallback", 
            exceptionsToIgnore = IllegalArgumentException.class)
    public String annotationDemo(@RequestParam(required = false) Integer id) {
        if (id < 0) {
            throw new IllegalArgumentException("id 参数不能为空");
        }else if (id > 10) {
            int v = id /0;
        }
        return "success" ;
    }
    /**
     * // BlockHandler 处理参数
     * @param id
     * @param ex
     * @return
     */
    public String blockHandler(Integer id, BlockException ex) {
        return "block: " + ex.getClass().getSimpleName();
    }
    public String fallback(Integer id, Throwable throwable) {
        return "error: " + throwable.getClass().getSimpleName();
    }

重启服务。 打开 Sentinel 控制台。 对我们的资源添加流控规则 QPS单机阈值为 1 。 访问接口 ,访问接口,使用 id 参数为 负数。可以看到我们忽略了 IllegalArgumentException.class 。 可以看到这里抛出了这个异常,但是没有走fallback 方法。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第30张图片
我们使用 大于 10 的参数。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第31张图片
可以看到抛出的异常,被我们的 fallback 逻辑处理了。 如果疯狂访问接口。 正确的方法,就会发现会被限流了 走的是 blockHandler 指定的方法
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第32张图片

2.9 规则管理以及推送

集中管理及推送:集中管理以及推送规则, sentinel-core 提供了API 和 拓展接口来接受信息。 开发者需要根据自己的环境选择一个可靠的推送方式; 同时 规则最好在控制台集中飞过

监控:支持可靠,快速的实时监控和历史监控数据查询, sentinel-core 记录秒级的资源运行情况,并且提供 API 来拉取资源运行信息, 当机器大于一台以上的时候, 可以通过 DashBoard 来拉取 聚合, 并且存储这些信息。 这个时候, Dashboard 需要有一个存储媒介,来存储历史运行情况

权限控制: 区分用户角色,来进行操作。 生产环境上的权限控制是非常重要的,理论上只有管理员等高级用户才有权限去修改应用的规则。

一般来说, 规则的推送有下面三种模式:

推送模式 说明 优点 缺点
原始模式 API 将规则推送到客户端,并且直接更新到内存中,拓展写数据源,WritableDataSource 简单无任何依赖 不能保证一致性,规则保存在内存中,重启后就消失,严重不建议用于生产环境
Pull 模式 拓展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS, 文件等, 简单,无任何依赖。规则持久化 不保证一致性;实时性不保证, 拉取过于频繁,也有可能会有性能问题
PUSH 拓展读数据源(ReadableDataSource), 规则中心统一推送,客户端通过注册监听器这种方式时刻监听变化,比如使用 Nacos, Zookeeper 等配置中心。 这种方式有更好的实时性和一致性保证。 生产环境推荐。 规则持久化, 一致性, 快速。 引入第三方依赖

原始模式
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第33张图片
Pull 模式
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第34张图片
Push 模式
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第35张图片
原始模式,最开始演示的就是原始模式了。 Push 和 Pull 模式都是可以持久化的。 但是推荐生产环境下使用的只有 Push 模式。所以只演示Push 模式。 这边使用 nacos 作为数据源

1.1 使用 Nacos 作为数据源

引入依赖
pom.xml

        <dependency>
            <groupId>com.alibaba.cspgroupId>
            <artifactId>sentinel-datasource-nacosartifactId>
        dependency>

修改配置 文件 application.yml

server:
  port: 12001
spring:
  application:
    name: sgg-alibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        service: ${spring.application.name}
      config:
        group: DEFAULT_GROUP
        namespace:
        file-extension: yaml
        name: ${spring.application.name}
    sentinel:
      datasource:
        ds1:
          nacos:
            server-addr: 127.0.0.1:8848 # Nacos 服务器地址
            namespace: # Nacos 命名空间
            group-id: DEFAULT_GROUP # nacos 分组
            data-id: ${spring.application.name}-flow-rule
            data-type: json # 数据格式
            rule-type: FLOW # 规则类型

在Nacos 中创建配置集。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第36张图片
resource: 资源名。即限流规则的作用对象
count: 限流阈值
grade: 限流阈值类型 (QPS 或者 并发线程数)
limitApp : 流控针对的调用来源,若为 default 则不区分调用来源
strategy : 调用关系限流策略
controlBehavior : 流量控制效果 (直接拒绝, Warm Up, 匀速排队)

简单测试
重启 12001 服务。 打开我们的 Sentinel 控制台,找到流控规则,可以看到这边有一个已经存在的流控规则,是从Nacos数据源加载而来的。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第37张图片
按照规则快速访问 6 次。 最后一次会被拒绝
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第38张图片

2.11 整合Feign

创建一个服务消费者。把之前的 12001 当做服务提供者。 使用 Feign 进行 HTTP API 调用.
引入依赖

 <dependencies>
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
    dependency>
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    dependency>
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
    dependency>
    <dependency>
        <groupId>com.alibaba.cspgroupId>
        <artifactId>sentinel-datasource-nacosartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-openfeignartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-actuatorartifactId>
    dependency>
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
    dependency>
dependencies>

创建配置文件 application.yml

server:
  port: 13001
spring:
  application:
    name: sgg-alibaba-sentinel-consumer-service
  cloud:
    sentinel:
      enabled: true # 是否开启 默认为 true 开启
      eager: true # 是否饥饿加载。 false 默认关闭
      transport:
        dashboard: 127.0.0.1:8080 # Sentinel 控制台地址
      filter:
        url-patterns: /**  # 拦截请求的地址
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
feign:
  sentinel:
    enabled: true # 开启Sentinel 对 Feign 的支持。 默认 false 关闭

创建远程调用 12001 的服务

@FeignClient(name = "sgg-alibaba-sentinel-service", fallbackFactory = TestProviderFeignClientFallbackFactory.class)
public interface TestProviderFeignClient {

    @GetMapping(value = "/sentinel/testA")
    String testA();
}

@FeignClient 注解,声明一个 FeignClient 客户端
name : 同 value 属性, 设置 FeignClient 调用的服务名。
fallbackFactory : 设置 fallback 工厂类, fallback 的作用是,在于 Http 调用失败的抛出异常的时候, 提供 fallback 处理逻辑。

创建 TestProviderFeignClientFallbackFactory

@Component
public class TestProviderFeignClientFallbackFactory implements FallbackFactory<TestProviderFeignClient> {
    @Override
    public TestProviderFeignClient create(Throwable throwable) {

        return new TestProviderFeignClient() {
            @Override
            public String testA() {
                return "fallback:" + throwable.getClass().getSimpleName();
            }
        };
    }
}

创建 controller

@RestController
@Slf4j
@RequestMapping(value = "/consumer")
public class ConsumerController {
    @Autowired
    private TestProviderFeignClient testProviderFeignClient;

    @GetMapping(value = "/sentinel/testA")
    public String sentinelTestA(){
        return testProviderFeignClient.testA();
    }
}

创建启动类

@SpringBootApplication
@EnableFeignClients
public class AlibabaSentinelConsumer13001App {

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

@EnableFeignClients 声明开启 Feign客户端的功能。

简单测试
分别启动 12001, 13001 。 访问一下 13001 的接口。 保证资源初始化。启动时如果会报以下的错误。

Caused by: java.lang.AbstractMethodError: com.alibaba.cloud.sentinel.feign.SentinelContractHolder.parseAndValidatateMetadata(Ljava/lang/Class;)Ljava/util/List;
	at feign.ReflectiveFeign$ParseHandlersByName.apply(ReflectiveFeign.java:154) ~[feign-core-10.4.0.jar:na]
	at feign.ReflectiveFeign.newInstance(ReflectiveFeign.java:52) ~[feign-core-10.4.0.jar:na]
	at feign.Feign$Builder.target(Feign.java:251) ~[feign-core-10.4.0.jar:na]
	at org.springframework.cloud.openfeign.HystrixTargeter.target(HystrixTargeter.java:38) ~[spring-cloud-openfeign-core-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.cloud.openfeign.FeignClientFactoryBean.loadBalance(FeignClientFactoryBean.java:243) ~[spring-cloud-openfeign-core-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:272) ~[spring-cloud-openfeign-core-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:252) ~[spring-cloud-openfeign-core-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:171) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	... 32 common frames omitted

解决方案: 参考这篇文章 添加链接描述

访问接口。
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第39张图片
添加我们的流控规则
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第40张图片
配置完成之后点击新增
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第41张图片
直接连续访问两次。 可以看到Sentinel 流量控制是会拒绝掉其中的一次的。 返回结果为
SpringCloud(十二)SpringCloudAlibaba Sentinel 分布式系统的流量防卫兵_第42张图片
说明我们的Feign 是走了 fallback 的处理逻辑。

总结: 感谢B站尚硅谷的老师们

参考文章: 芋道源码 3Q

你可能感兴趣的:(Spring,Cloud,学习,SpringCloud,Sentinel)