服务熔断与限流:Sentinel

目录


配套资料,免费下载
链接:https://pan.baidu.com/s/1-eRFozbFIShqbqNRFD9KDw
提取码:rt3w
复制这段内容后打开百度网盘手机App,操作更方便哦

第一章 Sentinel的介绍

1.1、Sentinel是什么

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。提起 Spring Cloud 的限流降级组件,一般首先想到的是 Netflix 的 Hystrix。

服务熔断与限流:Sentinel

不过就在2018年底,Netflix 宣布不再积极开发 Hystrix,该项目将处于维护模式。官方表示 1.5.18 版本的 Hystrix 已经足够稳定,可以满足 Netflix 现有应用的需求,所以接下来其会把焦点转向对于自适应的实现,更多关注对应用程序的实时性能做出响应。对于新应用的熔断需求,将采用其它项目实现,Netflix 推荐了 Resilience4j。

作为 Spring Cloud Netflix 重要套件,Hystrix已经成为保障微服务稳定性的首选应用。其实除了 Netflix 和 Resilience4j,限流降级还有一个新的选择,就是阿里巴巴的 Sentinel 组件。

服务熔断与限流:Sentinel

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

  • 2012 年,Sentinel 诞生,主要功能为入口流量控制。
  • 2013-2017 年,Sentinel 在阿里巴巴集团内部迅速发展,成为基础技术模块,覆盖了所有的核心场景。Sentinel 也因此积累了大量的流量归整场景以及生产实践。
  • 2018 年,Sentinel 开源,并持续演进。
  • 2019 年,Sentinel 朝着多语言扩展的方向不断探索,推出 C++ 原生版本,同时针对 Service Mesh 场景也推出了 Envoy 集群流量控制支持,以解决 Service Mesh 架构下多语言限流的问题。
  • 2020 年,推出 Sentinel Go 版本,继续朝着云原生方向演进。

Sentinel 在经过阿里巴巴内部一系列秒杀大促,特别是双11这样电商大促中的锤炼,目前已有不少企业在使用开源版本和云版本的 Sentinel,包括顺丰、VIVO、每日优鲜、拼多多、易企秀、爱奇艺、融金所、VIPKID、喜马拉雅FM、百融金服等。

官方文档:https://github.com/alibaba/Sentinel/wiki

源码地址:https://github.com/alibaba/Sentinel

1.2、Sentinel的特性

Sentinel 有以下特征:

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 的主要特性:

服务熔断与限流:Sentinel

Sentinel 的开源生态:

服务熔断与限流:Sentinel

Sentinel 的组成部分:

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

1.3、Sentinel的对比

Sentinel Hystrix resilience4j
隔离策略 信号量隔离(并发控制) 线程池隔离/信号量隔离 信号量隔离
熔断降级策略 基于慢调用比例、异常比例、异常数 基于异常比例 基于异常比例、响应时间
实时统计实现 滑动窗口(LeapArray) 滑动窗口(基于 RxJava) Ring Bit Buffer
动态规则配置 支持近十种动态数据源 支持多种数据源 有限支持
扩展性 多个扩展点 插件的形式 接口的形式
基于注解的支持 支持 支持 支持
单机限流 基于 QPS,支持基于调用关系的限流 有限的支持 Rate Limiter
集群流控 支持 不支持 不支持
流量整形 支持预热模式与匀速排队控制效果 不支持 简单的 Rate Limiter 模式
系统自适应保护 支持 不支持 不支持
热点识别/防护 支持 不支持 不支持
多语言支持 Java/Go/C++ Java Java
Service Mesh 支持 支持 Envoy/Istio 不支持 不支持
控制台 提供开箱即用的控制台,可配置规则、实时监控、机器发现等 简单的监控查看 不提供控制台,可对接其它监控系统

第二章 Sentinel的安装与运行

2.1、下载Sentinel

下载地址:https://github.com/alibaba/Sentinel/releases/download/1.8.1/sentinel-dashboard-1.8.1.jar

2.2、运行Sentinel

运行命令:java -jar sentinel-dashboard-1.8.1.jar

2.3、访问Sentinel

访问地址:http://localhost:8080/,登录账户:sentinel,登录密码:sentinel

服务熔断与限流:Sentinel

服务熔断与限流:Sentinel

第三章 Sentinel的整合与演示

3.1、项目的准备与启动

(1)以单机模式启动nacos:startup.cmd -m standalone

(2)使用idea导入02-服务注册与配置:Nacos中的spring-cloud-alibaba-study

服务熔断与限流:Sentinel

3.2、商品服务整合哨兵

3.2.1、新增依赖

service-goodspom.xml中新增如下依赖:

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

3.2.2、新增配置

service-goodsapplication.yaml中新增如下配置:

spring:
  cloud:
    sentinel:
      transport:
        dashboard: http://localhost:8080

service-goodsbootstrap.yaml中注释如下配置:

spring:
  application:
    name: service-goods
  profiles:
    active: dev #激活开发环境配置
  cloud:
    nacos:
      discovery:
        server-addr: http://localhost:8848
#      config:
#        server-addr: http://localhost:8848
#        prefix: application #指定配置文件的前缀
#        file-extension: yaml #指定配置文件的拓展名
#        group: DEFAULT_GROUP #指定配置文件所在组
#        namespace: public #指定配置文件所在命名空间

service-goods中删除ReadConfigController控制类

3.2.3、实时监控

启动ServiceGoodsApplication7001,然后访问地址:http://localhost:7001/goods/findByGid?gid=1,然后查看Sentinel控制台:

服务熔断与限流:Sentinel

3.2.4、簇点链路

簇点链路:

在这个功能模块下,你可以清晰地看出当前资源的访问情况,并且可以为资源添加流控降级热点授权等规则,这些规则会在后边详细介绍。

树状视图:

服务熔断与限流:Sentinel

列表视图:

服务熔断与限流:Sentinel

相关概念:

每秒查询率(QPS) :每秒查询率QPS是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准,在因特网上,作为域名系统服务器的机器的性能经常用每秒查询率来衡量。对应fetches/sec,即每秒的响应请求数,也即是最大吞吐能力。

响应时间(RT) :响应时间是指系统对请求作出响应的时间。直观上看,这个指标与人对软件性能的主观感受是非常一致的,因为它完整地记录了整个计算机系统处理请求的时间。由于一个系统通常会提供许多功能,而不同功能的处理逻辑也千差万别,因而不同功能的响应时间也不尽相同,甚至同一功能在不同输入数据的情况下响应时间也不相同。所以,在讨论一个系统的响应时间时,人们通常是指该系统所有功能的平均时间或者所有功能的最大响应时间。当然,往往也需要对每个或每组功能讨论其平均响应时间和最大响应时间。对于单机的没有并发操作的应用系统而言,人们普遍认为响应时间是一个合理且准确的性能指标。需要指出的是,响应时间的绝对值并不能直接反映软件的性能的高低,软件性能的高低实际上取决于用户对该响应时间的接受程度。对于一个游戏软件来说,响应时间小于100毫秒应该是不错的,响应时间在1秒左右可能属于勉强可以接受,如果响应时间达到3秒就完全难以接受了。而对于编译系统来说,完整编译一个较大规模软件的源代码可能需要几十分钟甚至更长时间,但这些响应时间对于用户来说都是可以接受的。

第四章 Sentinel的流控规则

4.1、规则介绍

官方说明:https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

服务熔断与限流:Sentinel

  • 资源名:一般是我们的请求路径
  • 针对来源:是指来自于哪个应用
  • 阈值类型:分为QPS或线程数
  • 单机阈值:单个节点的QPS或线程数阈值
  • 是否集群:被请求的服务是否集群部署
  • 流控模式:
    • 直接,就是直接对该资源进行控制
    • 关联,关联某一个资源(/app2),被关联的资源/app2达到阈值2,则限制当前资源/test的访问
    • 链路,记录指定链路上的流量
  • 流控效果:(线程数阈值类型不存在此属性)
    • 快速失败 ,直接限制
    • Warm Up,根据coldFactor(默认为3)的值,从 阈值/coldFactor,经过预热的时长,才达到设置的QPS阈值,比如设置QPS阈值为100,那么100/3 =33,用33作为最初的阈值,然后在10秒到达100后再开始限流,这个coldFactor值,可以在配置文件中配置
    • 排队等待,在QPS阈值到达后,新的请求就等待,直到超时,可以适用于突发流量的请求

4.2、规则演示

4.2.1、流控模式:直接

(1)在GoodsController中,添加测试方法,然后重启,代码如下:

@RequestMapping("/goods/testA")
public String testA() {
    return "server port:" + port + ",testA...";
}

(2)访问测试方法,访问地址:http://localhost:7001/goods/testA

服务熔断与限流:Sentinel

(3)添加流控规则,如下图:

服务熔断与限流:Sentinel

(4)访问测试方法,快速连续访问多次:http://localhost:7001/goods/testA

服务熔断与限流:Sentinel

按照我们的配置,1秒钟以内只能有1个请求(单机阈值)通过,如果你在1秒以内快速连续访问,就会触发流量限制。

4.2.2、流控模式:关联

(1)在GoodsController中,添加测试方法,然后重启,代码如下:

@RequestMapping("/goods/testB")
public String testB() {
    return "server port:" + port + ",testB...";
}

(2)访问测试方法,访问地址:http://localhost:7001/goods/testA

服务熔断与限流:Sentinel

(3)访问测试方法,访问地址:http://localhost:7001/goods/testB

服务熔断与限流:Sentinel

(4)添加流控规则,如下图:

服务熔断与限流:Sentinel

(5)访问测试方法,并发访问:http://localhost:7001/goods/testB,这里可以采用JMeter或者Postman,然后访问:http://localhost:7001/goods/testA

服务熔断与限流:Sentinel

简单的来说:关联的/goods/testB资源一旦限流,当前资源/goods/testA也会被限流保护起来,但是测试的时候并不好测,需要掌握并发测试知识。

4.2.3、流控模式:链路

(1)添加流控规则,如下图:

服务熔断与限流:Sentinel

(2)访问测试方法,快速连续访问多次:http://localhost:7001/goods/testB

服务熔断与限流:Sentinel

NodeSelectorSlot 中记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一棵调用树。这棵树的根节点是一个名字为 machine-root 的虚拟节点,调用链的入口都是这个虚节点的子节点。

一棵典型的调用树如下图所示:

     	          machine-root
                    /       \
                   /         \
             Entrance1     Entrance2
                /             \
               /               \
      DefaultNode(nodeA)   DefaultNode(nodeA)

上图中来自入口 Entrance1Entrance2 的请求都调用到了资源 NodeA,Sentinel 允许只根据某个入口的统计信息对资源限流。比如我们可以设置 strategyRuleConstant.STRATEGY_CHAIN,同时设置 refResourceEntrance1 来表示只有从入口 Entrance1 的调用才会记录到 NodeA 的限流统计当中,而不关心经 Entrance2 到来的调用。

同理,我们现在对/goods/testB的限流都是经过sentinel_spring_web_context这个虚拟节点的,如下图:

服务熔断与限流:Sentinel

4.2.4、流控效果:快速失败

快速失败RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。我们之前所有的测试的流控效果都是快速失败,当失败会显示如下信息:

服务熔断与限流:Sentinel

4.2.5、流控效果:Warm Up

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

当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。

通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:

服务熔断与限流:Sentinel

4.2.6、流控效果:排队等待

排队等待RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。

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

这种方式适合用于请求以突刺状来到,这个时候我们不希望一下子把所有的请求都通过,这样可能会把系统压垮;同时我们也期待系统以稳定的速度,逐步处理这些请求,以起到“削峰填谷”的效果,而不是拒绝所有请求。

例如,如果系统使用 Apache RocketMQ 来收发消息,系统在某个时间突然收到大量消息。我们希望以固定的速率来处理消息,而不是一下子拒绝这些消息。这个时候可以使用匀速器,也就是给消息排队。效果如下所示:

服务熔断与限流:Sentinel

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

第五章 Sentinel的降级规则

5.1、规则介绍

官方说明:https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7

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

服务熔断与限流:Sentinel

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

服务熔断与限流:Sentinel

Sentinel 提供以下几种熔断策略:

  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

最小请求数:触发熔断的最小请求数目,若当前统计窗口内的请求数小于此值,即使达到熔断条件规则也不会触发。

注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常。开源整合模块,如 Sentinel Dubbo Adapter, Sentinel Web Servlet Filter 或 @SentinelResource 注解会自动统计业务异常,无需手动调用。

5.2、规则演示

5.2.1、熔断策略:慢调用比例

(1)修改service-goodsGoodsControllerfindByGid方法,然后重启,代码如下:

@RequestMapping("/goods/findByGid")
public String findByGid(@RequestParam("gid") String gid) {
    //模拟业务超时
    if ("0".equals(gid)) {
        try { Thread.sleep(500); } catch (Exception e) {}
    }
    //模拟业务正常
    if ("1".equals(gid)) {
        return "server port:" + port + ",findByGid:" + gid;
    }
    //模拟业务出错
    if ("2".equals(gid)) {
        int i = 1 / 0;
    }
    return "other ...";
}

(2)访问测试方法,访问地址:http://localhost:7001/goods/findByGid?gid=1

(3)添加降级规则,如下图:

服务熔断与限流:Sentinel

(4)访问超时方法,连续多次访问地址:http://localhost:7001/goods/findByGid?gid=0

服务熔断与限流:Sentinel

(5)如果此时访问正常的方法,访问地址:http://localhost:7001/goods/findByGid?gid=1

服务熔断与限流:Sentinel

(6)过一会,再次访问正常的业务地址,发现服务又恢复了

5.2.2、熔断策略:异常比例

(1)修改降级规则,如下图:

服务熔断与限流:Sentinel

(2)访问出错地址,连续多次访问地址:http://localhost:7001/goods/findByGid?gid=2

服务熔断与限流:Sentinel

(3)如果此时访问正常的方法,访问地址:http://localhost:7001/goods/findByGid?gid=1

服务熔断与限流:Sentinel

(4)过一会,再次访问正常的业务地址,发现服务又恢复了

5.2.3、熔断策略:异常数

(1)修改降级规则,如下图:

服务熔断与限流:Sentinel

(2)访问出错地址,连续多次访问地址:http://localhost:7001/goods/findByGid?gid=2

服务熔断与限流:Sentinel

(3)如果此时访问正常的方法,访问地址:http://localhost:7001/goods/findByGid?gid=1

服务熔断与限流:Sentinel

(4)过一会,再次访问正常的业务地址,发现服务又恢复了

第六章 Sentinel的热点规则

6.1、规则介绍

官方说明:https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81

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

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

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

Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。

服务熔断与限流:Sentinel

注意:热点规则需要使用@SentinelResource(“app”)注解,否则不生效,并且参数必须是7种基本数据类型才会生效。

6.2、规则演示

6.2.1、热点参数限流

(1)修改service-goodsGoodsControllerfindByGid方法,然后重启,代码如下:

@RequestMapping("/goods/findByGid")
@SentinelResource(value = "findByGidHotKey", blockHandler = "findByGidBlockHandler")
public String findByGid(@RequestParam("gid") String gid) {
    //模拟业务超时
    if ("0".equals(gid)) {
        try { Thread.sleep(500); } catch (Exception e) {}
    }
    //模拟业务正常
    if ("1".equals(gid)) {
        return "server port:" + port + ",findByGid:" + gid;
    }
    //模拟业务出错
    if ("2".equals(gid)) {
        int i = 1 / 0;
    }
    return "other ...";
}

//兜底方法
public String findByGidBlockHandler(String gid, BlockException exception) {
    return "限流了...";
}

(2)访问测试方法,访问地址:http://localhost:7001/goods/findByGid?gid=1

(3)添加热点规则,如下图:

服务熔断与限流:Sentinel

(4)访问测试地址,连续快速访问:http://localhost:7001/goods/findByGid?gid=1

服务熔断与限流:Sentinel

6.2.2、参数例外项

(1)修改热点规则,如下图:

服务熔断与限流:Sentinel

(2)访问测试地址,连续快速访问,发现并不会限流:http://localhost:7001/goods/findByGid?gid=10086

服务熔断与限流:Sentinel

第七章 Sentinel的授权规则

7.1、规则介绍

官方说明:https://github.com/alibaba/Sentinel/wiki/%E9%BB%91%E7%99%BD%E5%90%8D%E5%8D%95%E6%8E%A7%E5%88%B6

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

服务熔断与限流:Sentinel

7.2、规则演示

7.2.1、白名单测试

(1)在service-goods下,添加来源解析器,类名称com.caochenlei.config.MyRequestOriginParser

@Component
public class MyRequestOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
        String origin = request.getParameter("origin");
        return origin;
    }
}

(2)重启服务,然后访问地址:http://localhost:7001/goods/findByGid?gid=1

(3)添加授权规则,如下图:

服务熔断与限流:Sentinel

(4)访问测试地址,我们携带上允许的来源,访问地址:http://localhost:7001/goods/findByGid?gid=1&origin=myapp

服务熔断与限流:Sentinel

(5)如果我们换一个来源,则限流,访问地址:http://localhost:7001/goods/findByGid?gid=1&origin=aaa

服务熔断与限流:Sentinel

7.2.2、黑名单测试

(1)修改授权规则,如下图:

服务熔断与限流:Sentinel

(2)如果此时是myapp的来源进行访问,会被限流,访问地址:http://localhost:7001/goods/findByGid?gid=1&origin=myapp

服务熔断与限流:Sentinel

(3)如果我们不携带或者携带不是黑名单的来源,则不会限流,访问地址:http://localhost:7001/goods/findByGid?gid=1&origin=aaa

服务熔断与限流:Sentinel

第八章 Sentinel的系统规则

8.1、规则介绍

官方介绍: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

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

服务熔断与限流:Sentinel

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

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

8.2、规则演示

由于部分效果可能并不好测试,本章节暂不演示。

第九章 Sentinel的保护模式

9.1、默认地址埋点保护

Sentinel为Spring Boot程序提供了一个starter依赖,由于Sentinel starter依赖默认情况下就会为所有的HTTP服务提供限流埋点,所以在Spring Boot中的Controller都可以受到Sentinel的保护,当然,我们还需要为Sentinel配置保护的规则。

他的底层是通过一个拦截器对请求url进行拦截:com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor

(1)重新启动service-goods

(2)访问地址:http://localhost:7001/goods/testA

(3)添加限流规则,如下图:

服务熔断与限流:Sentinel

(4)快速连续访问地址:http://localhost:7001/goods/testA

服务熔断与限流:Sentinel

(5)可以在application.yaml中,通过如下配置关闭对微服务的保护,然后重启service-goods

server:
  port: 7001

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

spring:
  cloud:
    sentinel:
      transport:
        dashboard: http://localhost:8080
      #关闭sentinel对controller的url的保护
      filter:
        enabled: false

(6)访问地址:http://localhost:7001/goods/testA,无论你怎么访问,你都会发现,控制台根本就不显示簇点链路的信息了,如下图:

服务熔断与限流:Sentinel

9.2、手写代码埋点保护

我们在上一种方式中已经禁用了sentinel对controller的url的保护,接下来,我们将使用代码的形式进行保护。

(1)修改service-goodsGoodsController的方法testB,然后重启,代码如下:

@RequestMapping("/goods/testB")
public String testB() {
    Entry entry = null;
    try {
        entry = SphU.entry("testB");
        //以下请书写正常业务代码...
        return "server port:" + port + ",testB...";
    } catch (Exception e) {
        e.printStackTrace();
        Tracer.trace(e);//记录异常数量,对于异常比例和异常数的降级,这一句必须有

        if (e instanceof FlowException) {
            return "流量限流";//对应流控规则
        } else if (e instanceof DegradeException) {
            return "服务熔断降级";//对应降级规则
        } else if (e instanceof ParamFlowException) {
            return "热点参数限流";//对应热点规则
        } else if (e instanceof AuthorityException) {
            return "黑白名单控制";//对应授权规则
        } else if (e instanceof SystemBlockException) {
            return "系统自适应限流";//对应系统规则
        }

        return "Exception ...";//上边那一大串if...else也可以不写,直接返回这句
    } finally {
        if (entry != null) {
            entry.exit();
        }
    }
}

(2)访问地址:http://localhost:7001/goods/testB

(3)查看控制台的簇点链路,如下图:

服务熔断与限流:Sentinel

(4)添加流控规则,如下图:

服务熔断与限流:Sentinel

(5)快速连续访问地址:http://localhost:7001/goods/testB

服务熔断与限流:Sentinel

我们也发现了,这种手写代码虽然也可以实现,而且代码比较固定,虽然不难理解,但是会增加代码的冗余,很不方便,于是就有了下边这种注解方式。

9.3、使用注解埋点保护

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

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:

  • value:资源名称,必需项(不能为空)
  • entryType:entry 类型,可选项(默认为 EntryType.OUT
  • blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。
    • 返回值类型必须与原函数返回值类型一致,并且函数访问范围需要是 public
    • 方法参数列表需要和原函数一致,或者可以额外多一个 BlockException 类型的参数用于接收对应的异常。
    • blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • fallback / fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
    • 返回值类型必须与原函数返回值类型一致,并且函数访问范围需要是 public
    • 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
    • 返回值类型必须与原函数返回值类型一致,并且函数访问范围需要是 public
    • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

注意:

  • 1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理
  • 1.8.0 版本开始,defaultFallback 支持在类级别进行配置。
  • 注解方式埋点不支持 private 方法。

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandlerfallbackdefaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。

从 1.4.0 版本开始,注解方式定义资源支持自动统计业务异常,无需手动调用 Tracer.trace(ex) 来记录业务异常。Sentinel 1.4.0 以前的版本需要自行调用 Tracer.trace(ex) 来记录业务异常。

代码演示

(1)编写Sentinel控制台规则触发的兜底兜底方法,全类名:com.caochenlei.controller.MyBlockHandler

public class MyBlockHandler {
    public static String testC(@PathVariable("id") int id, BlockException blockException) {
        return "testC:" + id + ",限流了,msg:" + blockException.getMessage();
    }
}

(2)编写业务出错兜底方法,全类名:com.caochenlei.controller.MyFallback

public class MyFallback {
    public static String testC(@PathVariable("id") int id, Throwable t) {
        return "testC:" + id + ",降级了,msg:" + t.getMessage();
    }
}

(3)新增一个测试方法,代码如下:

@SentinelResource(value = "testC",
        blockHandler = "testC", blockHandlerClass = MyBlockHandler.class,//这一对是处理sentinel规则的兜底方法
        fallback = "testC", fallbackClass = MyFallback.class//这一对是处理当前控制器业务异常的熔断降级的兜底方法
)
//下边是你正常的业务逻辑和平时常用的注解,都不需要动,只需要单独标注一个@SentinelResource注解,告诉Sentinel这是一个埋点资源
@RequestMapping("/goods/testC/{id}")
public String testC(@PathVariable("id") int id) {
    //模拟业务正常
    if (id == 1) {
        return "server port:" + port + ",testC...";
    }
    //模拟业务出错
    if (id == 2) {
        int i = 1 / 0;
    }
    return "other ...";
}

(4)访问地址:http://localhost:7001/goods/testC/1

服务熔断与限流:Sentinel

(5)添加流控规则,如下图:

服务熔断与限流:Sentinel

(6)快速连续访问地址:http://localhost:7001/goods/testC/1

服务熔断与限流:Sentinel

(7)测试异常业务,看看会不会走兜底方法,访问地址:http://localhost:7001/goods/testC/2

服务熔断与限流:Sentinel

第十章 Sentinel与远程调用

10.1、RestTemplate整合Sentinel

10.1.1、开启Sentinel配置

修改service-goodsapplication.yaml,配置如下:

server:
  port: 7001

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

spring:
  cloud:
    sentinel:
      transport:
        dashboard: http://localhost:8080
      #开启sentinel对controller的url的保护
      filter:
        enabled: true

#开启RestTemplate对Sentinel的支持,默认为true
resttemplate:
  sentinel:
    enabled: true

10.1.2、整合Sentinel注解

修改service-goodsServiceGoodsApplication,代码如下:

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


    @SentinelRestTemplate(
            //以下两对配置可以不指定,不指定就使用默认的配置
            fallback = "fallback", fallbackClass = SentinelRestTemplateExceptionUtil.class,
            blockHandler = "blockHandler", blockHandlerClass = SentinelRestTemplateExceptionUtil.class
    )
    @LoadBalanced//负载均衡
    @Bean//注册组件
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

10.1.3、修改商品服务

(1)添加兜底处理类,类全名:com.caochenlei.controller.SentinelRestTemplateExceptionUtil

public class SentinelRestTemplateExceptionUtil {
    //限流后处理
    public static SentinelClientHttpResponse blockHandler
    (HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
        return new SentinelClientHttpResponse("custom blockHandler info");
    }

    //降级后处理
    public static SentinelClientHttpResponse fallback
    (HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
        return new SentinelClientHttpResponse("custom fallback info");
    }
}

(2)在GoodsController中添加新的测试方法,代码如下:

@Autowired
private RestTemplate restTemplate;

@RequestMapping("/goods/testD/{id}")
public String testD(@PathVariable("id") int id) {
    return restTemplate.getForObject("http://service-order/order/" + id, String.class);
}

(3)重新启动service-goods

10.1.4、修改订单服务

(1)在OrderController中添加新的测试方法,代码如下:

@RequestMapping("/order/{oid}")
public String find(@PathVariable("oid") int oid) {
    //模拟正常业务
    if (oid == 1) {
        return "正常返回订单信息";
    }
    //模拟异常业务
    if (oid == 2) {
        int i = 1 / 0;
    }
    return "other...";
}

(2)重新启动service-order

10.1.5、访问测试地址

(1)快速连续多次访问地址:http://localhost:7001/goods/testD/1

服务熔断与限流:Sentinel

(2)快速连续多次访问地址:http://localhost:7001/goods/testD/2

服务熔断与限流:Sentinel

10.1.6、添加流控规则

服务熔断与限流:Sentinel

10.1.7、添加降级规则

服务熔断与限流:Sentinel

10.1.8、测试最终效果

(1)快速连续多次访问地址:http://localhost:7001/goods/testD/1

服务熔断与限流:Sentinel

(2)快速连续多次访问地址:http://localhost:7001/goods/testD/2

服务熔断与限流:Sentinel

10.2、OpenFeign整合Sentinel

10.2.1、导入相关依赖

service-goodspom.xml导入相关依赖:

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

10.2.2、开启相关配置

修改service-goodsapplication.yaml导入相关配置:

server:
  port: 7001

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

spring:
  cloud:
    sentinel:
      transport:
        dashboard: http://localhost:8080
      #开启sentinel对controller的url的保护
      filter:
        enabled: true

#开启RestTemplate对Sentinel的支持,默认为true
resttemplate:
  sentinel:
    enabled: true

#开启feign对Sentinel的支持,默认为false
feign:
  sentinel:
    enabled: true

10.2.3、配置主启动类

修改service-goodsServiceGoodsApplication开启Feign功能:

@SpringBootApplication
@EnableFeignClients // 开启 Feign 功能
public class ServiceGoodsApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceGoodsApplication.class, args);
    }


    @SentinelRestTemplate(
            //以下两对配置可以不指定,不指定就使用默认的配置
            fallback = "fallback", fallbackClass = SentinelRestTemplateExceptionUtil.class,
            blockHandler = "blockHandler", blockHandlerClass = SentinelRestTemplateExceptionUtil.class
    )
    @LoadBalanced//负载均衡
    @Bean//注册组件
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

10.2.4、编写业务接口

(1)在service-goods下编写Feign的业务接口,调用远程的服务是订单服务,接口全名:com.caochenlei.service.FeignOrderService

@FeignClient(value = "service-order",fallback = FeignOrderServiceFallback.class)
public interface FeignOrderService {
    @RequestMapping("/order/{oid}")
    public String find(@PathVariable("oid") int oid);
}

(2)编写兜底的fallback类,类全名:com.caochenlei.service.FeignOrderServiceFallback

@Component
public class FeignOrderServiceFallback implements FeignOrderService{
    @Override
    public String find(int oid) {
        return "FeignOrderServiceFallback 限流/降级...";
    }
}

10.2.5、编写控制器类

service-goodsGoodsController下,添加新的方法,代码如下:

@Autowired
private FeignOrderService orderService;

@RequestMapping("/goods/testE/{id}")
public String testE(@PathVariable("id") int id) {
    return orderService.find(id);
}

然后重新启动service-goods

10.2.6、访问测试地址

(1)快速连续多次访问地址:http://localhost:7001/goods/testE/1

服务熔断与限流:Sentinel

(2)快速连续多次访问地址:http://localhost:7001/goods/testE/2

服务熔断与限流:Sentinel

10.2.7、添加流控规则

服务熔断与限流:Sentinel

10.2.8、测试流控规则

(1)快速连续多次访问地址:http://localhost:7001/goods/testE/1

服务熔断与限流:Sentinel

第十一章 Sentinel数据持久化

官方说明:https://github.com/alibaba/Sentinel/wiki/%E5%9C%A8%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Sentinel

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

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

11.1、原始模式

如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中:

服务熔断与限流:Sentinel

这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境。

11.2、Pull模式

ull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry 中。以本地文件数据源为例:

public class FileDataSourceInit implements InitFunc {

    @Override
    public void init() throws Exception {
        String flowRulePath = "xxx";

        ReadableDataSource<String, List<FlowRule>> ds = new FileRefreshableDataSource<>(
            flowRulePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {})
        );
        // 将可读数据源注册至 FlowRuleManager.
        FlowRuleManager.register2Property(ds.getProperty());

        WritableDataSource<List<FlowRule>> wds = new FileWritableDataSource<>(flowRulePath, this::encodeJson);
        // 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.
        // 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.
        WritableDataSourceRegistry.registerFlowDataSource(wds);
    }

    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}

本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。以本地文件数据源为例,推送过程如下图所示:

服务熔断与限流:Sentinel

首先 Sentinel 控制台通过 API 将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。使用 pull 模式的数据源时一般不需要对 Sentinel 控制台进行改造。

这种实现方法好处是简单,不引入新的依赖,坏处是无法保证监控数据的一致性。

11.2.1、编写代码

service-goods下创建类com.caochenlei.FileDataSourceInit

/**
 * 规则持久化
 */
public class FileDataSourceInit implements InitFunc {
    @Override
    public void init() throws Exception {
        //可以根据需要指定规则文件的位置
        String ruleDir = System.getProperty("user.home") + "/sentinel/rules";

        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath = ruleDir + "/authority-rule.json";

        this.mkdirIfNotExits(ruleDir);

        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(paramFlowRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);

        // 流控规则:可读数据源
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath,
                flowRuleListParser
        );
        // 将可读数据源注册至FlowRuleManager
        // 这样当规则文件发生变化时,就会更新规则到内存
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());

        // 流控规则:可写数据源
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        // 将可写数据源注册至transport模块的WritableDataSourceRegistry中
        // 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);

        // 降级规则:可读数据源
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
                degradeRulePath,
                degradeRuleListParser
        );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());

        // 降级规则:可写数据源
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
                degradeRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

        // 热点参数规则:可读数据源
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
                paramFlowRulePath,
                paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());

        // 热点参数规则:可写数据源
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
                paramFlowRulePath,
                this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);

        // 系统规则:可读数据源
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
                systemRulePath,
                systemRuleListParser
        );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());

        // 系统规则:可写数据源
        WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
                systemRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

        // 授权规则:可读数据源
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
                authorityRulePath,
                authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());

        // 授权规则:可写数据源
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
                authorityRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
    }

    private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<FlowRule>>() {}
    );

    private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {}
    );

    private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<SystemRule>>() {}
    );

    private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<AuthorityRule>>() {}
    );

    private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<ParamFlowRule>>() {}
    );

    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}

11.2.2、配置代码

service-goodsresources下创建文件夹META-INF\services,然后创建文件:META-INF\services\com.alibaba.csp.sentinel.init.InitFunc

com.caochenlei.FileDataSourceInit

11.2.3、重启工程

重新启动service-goods,然后访问地址:http://localhost:7001/goods/testA

服务熔断与限流:Sentinel

11.2.4、添加流控规则

服务熔断与限流:Sentinel

我们这一章主要学习如何持久化,而不是各种限流测试,添加规则以后,我们就会发现规则已经持久化到了本地指定文件中了

服务熔断与限流:Sentinel

11.2.5、添加降级规则

服务熔断与限流:Sentinel

我们这一章主要学习如何持久化,而不是各种限流测试,添加规则以后,我们就会发现规则已经持久化到了本地指定文件中了

服务熔断与限流:Sentinel

11.2.6、添加热点规则

服务熔断与限流:Sentinel

我们这一章主要学习如何持久化,而不是各种限流测试,添加规则以后,我们就会发现规则已经持久化到了本地指定文件中了

服务熔断与限流:Sentinel

11.2.7、添加授权规则

服务熔断与限流:Sentinel

我们这一章主要学习如何持久化,而不是各种限流测试,添加规则以后,我们就会发现规则已经持久化到了本地指定文件中了

服务熔断与限流:Sentinel

11.2.8、添加系统规则

服务熔断与限流:Sentinel

我们这一章主要学习如何持久化,而不是各种限流测试,添加规则以后,我们就会发现规则已经持久化到了本地指定文件中了

服务熔断与限流:Sentinel

11.2.9、重启以后查看

重新启动service-goods,然后访问地址:http://localhost:7001/goods/testA

服务熔断与限流:Sentinel

流控规则

服务熔断与限流:Sentinel

降级规则

服务熔断与限流:Sentinel

热点规则

服务熔断与限流:Sentinel

授权规则

服务熔断与限流:Sentinel

系统规则

服务熔断与限流:Sentinel

为了下一小节的演示,请将META-INF\services\com.alibaba.csp.sentinel.init.InitFunc里边的内容删除,然后重启检查发现规则已经全部取消。

11.3、Push模式

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

服务熔断与限流:Sentinel

我们提供了 ZooKeeper, Apollo, Nacos 等的动态数据源实现。以 ZooKeeper 为例子,如果要使用第三方的配置中心作为配置管理,您需要做下面的几件事情:

  1. 实现一个公共的 ZooKeeper 客户端用于推送规则,在 Sentinel 控制台配置项中需要指定 ZooKeeper 的地址,启动时即创建 ZooKeeper Client。
  2. 我们需要针对每个应用(appName),每种规则设置不同的 path(可随时修改);或者约定大于配置(如 path 的模式统一为 /sentinel_rules/{appName}/{ruleType},e.g. sentinel_rules/appA/flowRule)。
  3. 规则配置页需要进行相应的改造,直接针对应用维度进行规则配置;修改同个应用多个资源的规则时可以批量进行推送,也可以分别推送。Sentinel 控制台将规则缓存在内存中(如 InMemFlowRuleStore),可以对其进行改造使其支持应用维度的规则缓存(key 为 appName),每次添加/修改/删除规则都先更新内存中的规则缓存,然后需要推送的时候从规则缓存中获取全量规则,然后通过上面实现的 Client 将规则推送到 ZooKeeper 即可。
  4. 应用客户端需要注册对应的读数据源以监听变更,可以参考 相关文档

从 Sentinel 1.4.0 开始,Sentinel 控制台提供 DynamicRulePublisherDynamicRuleProvider 接口用于实现应用维度的规则推送和拉取,并提供了相关的示例。Sentinel 提供应用维度规则推送的示例页面(/v2/flow),用户改造控制台对接配置中心后可直接通过 v2 页面推送规则至配置中心。改造详情可参考 应用维度规则推送示例

部署多个控制台实例时,通常需要将规则存至 DB 中,规则变更后同步向配置中心推送规则。

11.3.1、导入依赖

service-goodspom.xml中新增以下依赖:

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

11.3.2、编写配置

service-goodsapplication.yaml修改成如下配置:

server:
  port: 7001

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

spring:
  cloud:
    sentinel:
      transport:
        dashboard: http://localhost:8080
      #开启sentinel对controller的url的保护
      filter:
        enabled: true
      datasource:
        ds1:
          nacos:
            serverAddr: ${spring.cloud.nacos.discovery.server-addr}
            dataId: flow-rule.json
            groupId: DEFAULT_GROUP
            dataType: json
            ruleType: flow
        ds2:
          nacos:
            serverAddr: ${spring.cloud.nacos.discovery.server-addr}
            dataId: degrade-rule.json
            groupId: DEFAULT_GROUP
            dataType: json
            ruleType: degrade
        ds3:
          nacos:
            serverAddr: ${spring.cloud.nacos.discovery.server-addr}
            dataId: param-flow-rule.json
            groupId: DEFAULT_GROUP
            dataType: json
            ruleType: param-flow
        ds4:
          nacos:
            serverAddr: ${spring.cloud.nacos.discovery.server-addr}
            dataId: authority-rule.json
            groupId: DEFAULT_GROUP
            dataType: json
            ruleType: authority
        ds5:
          nacos:
            serverAddr: ${spring.cloud.nacos.discovery.server-addr}
            dataId: system-rule.json
            groupId: DEFAULT_GROUP
            dataType: json
            ruleType: system

#开启RestTemplate对Sentinel的支持,默认为true
resttemplate:
  sentinel:
    enabled: true

#开启feign对Sentinel的支持,默认为false
feign:
  sentinel:
    enabled: true

11.3.3、创建文件

访问地址:http://localhost:8848/nacos/,登录账号:nacos,登录密码:nacos

服务熔断与限流:Sentinel

依次创建如下文件,文件内容拷贝之前生成到本地的内容数据即可。

服务熔断与限流:Sentinel

服务熔断与限流:Sentinel

11.3.4、重启应用

重新启动service-goods,然后访问地址:http://localhost:7001/goods/testA

服务熔断与限流:Sentinel

流控规则

服务熔断与限流:Sentinel

降级规则

服务熔断与限流:Sentinel

热点规则

服务熔断与限流:Sentinel

授权规则

服务熔断与限流:Sentinel

系统规则

服务熔断与限流:Sentinel

你修改了以后是不能保存到nacos配置中心的,关于这个问题,可能会在以后的版本中进行增强。

第十二章 Sentinel的其他流控

12.1、集群流控

官方说明:https://github.com/alibaba/Sentinel/wiki/%E9%9B%86%E7%BE%A4%E6%B5%81%E6%8E%A7

12.2、网关流控

官方说明:https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81

上一篇:电商订单综合小项目


下一篇:thinkphp5.0 Db类操作数据库