Spring Cloud Alibaba - Sentinel 分布式系统流量哨兵

在这里插入图片描述

目录

  • 概述
    • 特征
    • 基本概念
  • 安装Sentinel
  • 微服务引入Sentinel案例
  • 流控规则(流量控制)
    • 流控模式-直接
    • 流控模式-关联
    • 流控模式-链路
    • 流控效果-快速失败
    • 流控效果-预热WarmUp
    • 流控效果-排队等候
  • 流控规则(并发线程数控制)
  • 熔断规则(熔断降级)
    • 慢调用比例
      • 案例演示
    • 异常比例
      • 案例演示
    • 异常数
      • 案例演示
  • @SentinelResource注解
  • 热点规则(热点参数限流)
  • 授权规则(黑白名单控制)
  • 规则持久化(规则入Nacos)
  • OpenFeign和Sentinel集成实现fallback服务降级
  • GateWay和Sentinel集成实现服务限流
  • 相关文献

概述

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。

一句话简单概括,Sentinel是一种流量治理的组件,作用等价于Spring Cloud Circurk Breaker。

特征

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

Sentinel 的主要特性:
在这里插入图片描述

基本概念

资源

资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。

只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。

规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。(Sentinel控制台、yml配置、java代码都可设定规则)

安装Sentinel

Sentinel 的使用可以分为两个部分:

  • 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等。

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

下载完后找到jar包,运行命令启动Sentinel控制台

java -jar sentinel-dashboard-xxx.jar

本地环境必须要jdk8版本以上,且8080端口未被占用(Sentinel 控制台端口)
从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel,访问网址:http://localhost:8080
在这里插入图片描述

微服务引入Sentinel案例

新建一个微服务,引入Nacos和Sentinel,将服务注册进Nacos,对服务进行流量监控和熔断降级

引入依赖

 <!--SpringCloud alibaba sentinel --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><!--nacos-discovery--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>

修改yml配置

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

启动类

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

业务类

@RestController
public class FlowLimitController
{@GetMapping("/testA")public String testA(){return "------testA";}@GetMapping("/testB")public String testB(){return "------testB";}
}

启动8401服务,这时候查看Sentinel控制台发现什么都没有,因为实际接口并未访问,Sentinel采用的是懒加载,不访问不监控,所以请求接口,http://localhost8401/testA,http://localhost:8401/testB,效果如下图
在这里插入图片描述

流控规则(流量控制)

在这里插入图片描述

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

参数含义
资源名资源的唯一名称,默认就是请求的接口路径,可以自行修改,但是要保证唯一。
针对来源具体针对某个微服务进行限流,默认值为default,表示不区分来源,全部限流。
阈值类型QPS表示通过QPS进行限流,并发线程数表示通过并发线程数限流。
单机阈值与阈值类型组合使用。如果阈值类型选择的是QPS,表示当调用接口的QPS达到阈值时,进行限流操作。如果阈值类型选择的是并发线程数,则表示当调用接口的并发线程数达到阈值时,进行限流操作。
是否集群选中则表示集群环境,不选中则表示非集群环境。

流量控制主要有两种统计类型,一种是统计并发线程数,另外一种则是统计 QPS,其中,0 代表根据并发数量来限流,1 代表根据 QPS 来进行流量控制

流控模式有三种:直接、关联、链路

流控模式-直接

默认的流控模式,当接口达到限流条件时,直接开启限流功能
表示1秒钟内查询1次就是OK,若超过次数1,就直接-快速失败,报默认错误
在这里插入图片描述
快速多次调用http://localhost8401/testA,会出现Blocked by Sentinel (flow limiting),限流成功

流控模式-关联

当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db 和 write_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的:设置 strategy 为 RuleConstant.STRATEGY_RELATE 同时设置 refResource 为 write_db。这样当写库操作过于频繁时,读数据的请求会被限流。

简单来说就是:B惹事,A挂了

控制台演示配置:当配置关联资源B的qps阈值超过1时,就限流A的访问地址
在这里插入图片描述
使用jmeter设置并发3秒内疯狂访问http://localhost:8401/testB,在不启动jmeter之前访问http://localhost:8401/testA,返回正常,这时候启动jmeter
在这里插入图片描述
在这里插入图片描述
这时候再访问/testA,会发现返回Blocked by Sentinel (flow limiting),等到jmeter调用结束后再访问/testA服务限流结束可正常访问,效果正确则配置成功

在这里我理解的是,AB服务之间,会优先确保B服务正常的一种限流策略,实际使用场景根据业务要求来设置。

流控模式-链路

来自不同链路请求对同一个目标访问时,实施针对性的限流措施,比如C来访问就限流,D来访问不限流

修改8401微服务的yml,关键配置【web-context-unify】默认true,设置为false

server:port: 8401spring:application:name: cloudalibaba-sentinel-service #8401微服务提供者后续将会被纳入阿里巴巴sentinel监管cloud:nacos:discovery:server-addr: localhost:8848         #Nacos服务注册中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard控制台服务地址port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口web-context-unify: false # controller层的方法对service层调用不认为是同一个根链路

新建service

@Service
public class FlowLimitService
{@SentinelResource(value = "common")public void common(){System.out.println("------FlowLimitService come in");}
}

controller新增方法

/**流控-链路演示demo* C和D两个请求都访问flowLimitService.common()方法,阈值到达后对C限流,对D不管*/@Resource private FlowLimitService flowLimitService;@GetMapping("/testC")public String testC(){flowLimitService.common();return "------testC";}@GetMapping("/testD")public String testD(){flowLimitService.common();return "------testD";}

控制台新增配置,C和D两个请求都访问flowLimitService.common()方法,对C限流,对D不管
在这里插入图片描述
访问http://localhost:8401/testC,超过一秒钟一次后,就发生限流
在这里插入图片描述
http://localhost:8401/testD,正常访问

流控效果-快速失败

快速失败也是直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式,是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。

上面几个流控规则演示的就是快速失败案例。

流控效果-预热WarmUp

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

这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等。通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
在这里插入图片描述
它的实现是在 Guava 的算法的基础上实现的。默认 coldFactor(冷却因子) 为 3(默认值),即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。

源码
在这里插入图片描述
案例,单机阈值为10,预热时长设置5秒。
系统初始化的阈值为10 / 3 约等于3,即单机阈值刚开始为3(我们人工设定单机阈值是10,sentinel计算后QPS判定为3开始);
然后过了5秒后阀值才慢慢升高恢复到设置的单机阈值10,也就是说5秒钟内QPS为3,过了保护期5秒后QPS为10

控制台配置
在这里插入图片描述
多次点击http://localhost:8401/testB,刚开始不行(被限流了),后面慢慢恢复就ok了

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

流控效果-排队等候

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

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

例如,如果系统使用 Apache RocketMQ 来收发消息,系统在某个时间突然收到大量消息。我们希望以固定的速率来处理消息,而不是一下子拒绝这些消息。这个时候可以使用匀速器,也就是给消息排队。效果如下所示:
在这里插入图片描述
Sentinel 匀速排队等待策略是 Leaky Bucket 算法结合虚拟队列等待机制实现的。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。

8401服务controller代码新增

@GetMapping("/testE")
public String testE()
{System.out.println(System.currentTimeMillis()+"      testE,排队等待");return "------testE";
}

jmeter
在这里插入图片描述
Sentinel控制台
在这里插入图片描述
按照单机阈值,一秒钟通过一个请求,10秒后的请求作为超时处理,放弃
在这里插入图片描述

流控规则(并发线程数控制)

并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。
在这里插入图片描述

结合上图,简单来说就是,Sentinel设置的单机阈值为1,用jmeter去设置10s并发请求/testB,这时候如果jmeter没有结束请求线程一直在占用的话,此时我们再用其他方式去调用/testB,是直接拒绝的效果,所以有且只有一个直接拒绝的流控效果。

熔断规则(熔断降级)

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

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

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

  • 慢调用比例 (SLOW_REQUEST_RATIO)
  • 异常比例 (ERROR_RATIO)
  • 异常数 (ERROR_COUNT)

慢调用比例

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

看图理解:
graphic
1.调用:一个请求发送到服务器,服务器给与响应,一个响应就是一个调用。
2.最大RT:即最大的响应时间,指系统对请求作出响应的业务处理时间。
3.慢调用:处理业务逻辑的实际时间>设置的最大RT时间,这个调用叫做慢调用。
4.慢调用比例:在所以调用中,慢调用占有实际的比例=慢调用次数➗总调用次数
5.比例阈值:自己设定的 , 比例阈值=慢调用次数➗调用次数
6.统计时长:时间的判断依据
7.最小请求数:设置的调用最小请求数,上图比如1秒钟打进来10个线程(大于我们配置的5个了)调用被触发

进入熔断状态判断依据:在统计时长内,实际请求数目>设定的最小请求数 且 实际慢调用比例>比例阈值 ,进入熔断状态。

1熔断状态(保险丝跳闸断电,不可访问):在接下来的熔断时长内请求会自动被熔断

2探测恢复状态(探路先锋):熔断时长结束后进入探测恢复状态

3结束熔断(保险丝闭合恢复,可以访问):在探测恢复状态,如果接下来的一个请求响应时间小于设置的慢调用 RT,则结束熔断,否则继续熔断。

案例演示

10个线程,在一秒的时间内发送完。又因为服务器响应时长设置:暂停1秒,所以响应一个请求的时长都大于1秒综上符合熔断条件,所以当线程开启1秒后,进入熔断状态

8401服务controller修改

/*** 新增熔断规则-慢调用比例* @return*/
@GetMapping("/testF")
public String testF()
{//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println("----测试:新增熔断规则-慢调用比例 ");return "------testF 新增熔断规则-慢调用比例";
}

控制台配置
在这里插入图片描述
jmeter压测
在这里插入图片描述
按照上述配置,熔断触发:

多次循环,一秒钟打进来10个线程(大于5个了)调用/testF

假如在统计时长内,实际请求数目>最小请求数且慢调用比例>比例阈值 ,断路器打开(保险丝跳闸)微服务不可用(Blocked by Sentinel (flow limiting)),进入熔断状态5秒;后续停止jmeter,没有这么大的访问量了,单独用浏览器访问rest地址,断路器关闭(保险丝恢复,合上闸口),

微服务恢复OK

异常比例

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

案例演示

controller

/*** 新增熔断规则-异常比例* @return*/
@GetMapping("/testG")
public String testG()
{System.out.println("----测试:新增熔断规则-异常比例 ");int age = 10/0;return "------testG,新增熔断规则-异常比例 ";
}

控制台
在这里插入图片描述
jmeter
在这里插入图片描述
启动jmeter后访问/testG,配置成功
在这里插入图片描述

异常数

当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

案例演示

controller

/*** 新增熔断规则-异常数* @return*/
@GetMapping("/testH")
public String testH()
{System.out.println("----测试:新增熔断规则-异常数 ");int age = 10/0;return "------testH,新增熔断规则-异常数 ";
}

控制台
在这里插入图片描述
jmeter
在这里插入图片描述
启动jmeter开工,上述配置表示,在1秒钟内最少请求2次,当异常数大于1时,会触发熔断操作断路器开启(保险丝跳闸),微服务不可用了,熔断的时长为5秒,不再报错error而是服务降级了出提示Blocked by Sentinel (flow limiting)

@SentinelResource注解

SentinelResource是一个流量防卫防护组件注解用于指定防护资源,对配置的资源进行流量控制、熔断降级等功能,

查看SentinelResource 源码,可以得知有三个属性很重要

  • value:资源名称
  • blockHandler:处理BlockException的函数名称,函数要求1. 必须是 public2.返回类型 参数与原方法一致3. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置blockHandlerClass ,并指定blockHandlerClass里面的方法。
  • fallback:用于在抛出异常的时候提供fallback处理逻辑。 fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求:1. 返回类型与原方法一致2. 参数类型需要和原方法相匹配3. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置fallbackClass ,并指定fallbackClass里面的方法。

代码演示(Sentinel控制台自行创建熔断规则,上面有很多案例演示,本次演示就不多说了)

@GetMapping("/rateLimit/doAction/{p1}")@SentinelResource(value = "doActionSentinelResource",blockHandler = "doActionBlockHandler", fallback = "doActionFallback")public String doAction(@PathVariable("p1") Integer p1) {if (p1 == 0){throw new RuntimeException("p1等于零直接异常");}return "doAction";}public String doActionBlockHandler(@PathVariable("p1") Integer p1,BlockException e){log.error("sentinel配置自定义限流了:{}", e);return "sentinel配置自定义限流了";}public String doActionFallback(@PathVariable("p1") Integer p1,Throwable e){log.error("程序逻辑异常了:{}", e);return "程序逻辑异常了"+"\t"+e.getMessage();}

可见代码上面配置了三个属性,分三种场景来说明

  1. 只设置了value:限流生效的话,默认返回限流提示语Blocked by Sentinel (flow limiting)
  2. 设置value+blockHandler:限流生效,返回自定义提示语如:sentinel配置自定义限流了
  3. 设置value+blockHandler+fallback:限流生效,返回自定义提示语如:sentinel配置自定义限流了,但如果程序出现运行时异常,提示自定义语句:程序逻辑异常了xxxxx

blockHandler,主要针对sentinel配置后出现的违规情况处理
fallback,程序异常了JVM抛出的异常服务降级
两者可共存,具体案例演示可自行编写测试

热点规则(热点参数限流)

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

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

服务新增接口

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

控制台
在这里插入图片描述
限流模式只支持QPS模式,固定写死了。

@SentinelResource注解的方法参数索引,0代表第一个参数,1代表第二个参数,以此类推

单机阀值以及统计窗口时长表示在此窗口时间超过阀值就限流。

上面的抓图就是第一个参数有值的话,1秒的QPS为1,超过就限流,限流后调用dealHandler_testHotKey支持方法。

效果

  • 访问http://localhost:8401/testHotKey?p1=abc,含有参数P1,当每秒访问的频率超过1次时,会触发Sentinel的限流操作
  • 访问http://localhost:8401/testHotKey?p1=abc&p2=33,当每秒访问的频率超过1次时,会触发Sentinel的限流操作
  • 访问http://localhost:8401/testHotKey?p2=33,没有热点参数P1,不会被限流正常访问

参数例外项
如果我们希望P1能够对指定数值进行限流也是可以的。
在这里插入图片描述
效果:

  • 访问http://localhost:8401/testHotKey?p1=5,当p1等于5的时候,阈值变为200,达到200阈值后才会被限流
  • 访问http://localhost:8401/testHotKey?p1=2,当p1等于2的时候,阈值还是1立马限流

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

授权规则(黑白名单控制)

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

案例演示
新建controller

@RestController
@Slf4j
public class EmpowerController //Empower授权规则,用来处理请求的来源
{@GetMapping(value = "/empower")public String requestSentinel4(){log.info("测试Sentinel授权规则empower");return "Sentinel授权规则";}
}

新建配置类

@Component
public class MyRequestOriginParser implements RequestOriginParser
{@Overridepublic String parseOrigin(HttpServletRequest httpServletRequest) {return httpServletRequest.getParameter("serverName");}
}

启动8401服务,访问http://localhost:8401/empower成功
控制台
在这里插入图片描述
效果

  • 访问http://localhost:8401/empower?serverName=test,http://localhost:8401/empower?serverName=test2被限流,显示限流默认提示语
    • 访问http://localhost:8401/empower?serverName=aa,正常返回数据

因为test和test2在控制台被设置了黑名单,因此其他的才能访问成功

规则持久化(规则入Nacos)

到此为止,如果服务一旦重启,Sentinel控制台配置的规则就会被重置,因此需要将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,Sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上Sentinel上的流控规则持续有效

修改8401服务,引入Nacos依赖

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

修改yml

server:port: 8401spring:application:name: cloudalibaba-sentinel-service #8401微服务提供者后续将会被纳入阿里巴巴sentinel监管cloud:nacos:discovery:server-addr: localhost:8848         #Nacos服务注册中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard控制台服务地址port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口web-context-unify: false # controller层的方法对service层调用不认为是同一个根链路datasource:ds1:nacos:server-addr: localhost:8848dataId: ${spring.application.name}groupId: DEFAULT_GROUPdata-type: jsonrule-type: flow # com.alibaba.cloud.sentinel.datasource.RuleType

rule-type含义值见源码
在这里插入图片描述
在这里插入图片描述
把规则配置添加到Nacos
在这里插入图片描述

resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。

这时候停止8401,Sentinel控制台没有规则(被重置了),重启8401,调用8401服务接口,过一会,配置的规则就会出来,规则持久化生效。

OpenFeign和Sentinel集成实现fallback服务降级

每个微服务都有自己的fallback服务降级处理,会有几个问题

  • fallback过多,不易管理
  • 代码不整洁(每个controller的api都有自己的fallback,重复代码过多)

那么可以使用FeignClient的fallback,走统一的服务降级

本次案例三个微服务,nacos-payment-provider(服务提供方)、cloud-api-commons(统一的服务降级处理)、nacos-order-consumer(服务调用方)

服务提供方
引入依赖

<!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--alibaba-sentinel--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><!-- 引入自己定义的api通用包 --><dependency><groupId>com.demo.cloud</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency>

配置yml

server:port: 9001spring:application:name: nacos-payment-providercloud:nacos:discovery:server-addr: localhost:8848 #配置Nacos地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard控制台服务地址port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口

启动类

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

controller

@Value("${server.port}")private String serverPort;@GetMapping(value = "/pay/nacos/{id}")public String getPayInfo(@PathVariable("id") Integer id){return "nacos registry, serverPort: "+ serverPort+"\t id"+id;}@GetMapping("/pay/nacos/get/{orderNo}")@SentinelResource(value = "getPayByOrderNo",blockHandler = "handlerBlockHandler")public ResultData getPayByOrderNo(@PathVariable("orderNo") String orderNo){//模拟从数据库查询出数据并赋值给DTOPayDTO payDTO = new PayDTO();payDTO.setId(1024);payDTO.setOrderNo(orderNo);payDTO.setAmount(BigDecimal.valueOf(9.9));payDTO.setPayNo("pay:"+IdUtil.fastUUID());payDTO.setUserId(1);return ResultData.success("查询返回值:"+payDTO);}public ResultData handlerBlockHandler(@PathVariable("orderNo") String orderNo,BlockException exception){return ResultData.fail(ReturnCodeEnum.RC500.getCode(),"getPayByOrderNo服务不可用," +"触发sentinel流控配置规则"+"\t"+"o(╥﹏╥)o");}

启动项目,调用接口自测,http://localhost:9001/pay/nacos/get/1024

cloud-api-commons
引入依赖

<!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--alibaba-sentinel--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>

controller

@FeignClient(value = "nacos-payment-provider",fallback = PayFeignSentinelApiFallBack.class)
public interface PayFeignSentinelApi
{@GetMapping("/pay/nacos/get/{orderNo}")public ResultData getPayByOrderNo(@PathVariable("orderNo") String orderNo);
}

为远程调用新建全局统一服务降级类

@Component
public class PayFeignSentinelApiFallBack implements PayFeignSentinelApi
{@Overridepublic ResultData getPayByOrderNo(String orderNo){return ResultData.fail(ReturnCodeEnum.RC500.getCode(),"对方服务宕机或不可用,FallBack服务降级o(╥﹏╥)o");}
}

服务调用方
引入依赖

<!-- 引入自己定义的api通用包 --><dependency><groupId>com.demo.cloud</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency><!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--alibaba-sentinel--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>

修改yml

server:port: 83spring:application:name: nacos-order-consumercloud:nacos:discovery:server-addr: localhost:8848
#消费者将要去访问的微服务名称(nacos微服务提供者叫什么你写什么)
service-url:nacos-user-service: http://nacos-payment-provider# 激活Sentinel对Feign的支持
feign:sentinel:enabled: true

启动类

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

controller

@RestController
public class OrderNacosController
{@Resourceprivate PayFeignSentinelApi payFeignSentinelApi;@GetMapping(value = "/consumer/pay/nacos/get/{orderNo}")public ResultData getPayByOrderNo(@PathVariable("orderNo") String orderNo){return payFeignSentinelApi.getPayByOrderNo(orderNo);}
}

启动消费者服务,如果出现以下报错,检查springboot和springcloud的版本问题,会导致Sentinel版本不兼容
在这里插入图片描述
这样两个服务都启动了,调用http://lochost:83/consumer/pay/nacos/get/1024,正常返回
这时候Sentinel控制台设置qps流量规则
在这里插入图片描述
多次高频调用http://lochost:83/consumer/pay/nacos/get/1024,返回【getPayByOrderNo服务不可用,触发sentinel流控配置规则】,出发限流成功。

但是大家很好奇,为什么我的服务降级没有提示呢,这时候把9001也就是服务提供者给停掉,再去调用http://lochost:83/consumer/pay/nacos/get/1024,就会提示【对方服务宕机或不可用,FallBack服务降级o(╥﹏╥)o】

GateWay和Sentinel集成实现服务限流

新建网关服务
pom

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-transport-simple-http</artifactId><version>1.8.6</version></dependency><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-spring-cloud-gateway-adapter</artifactId><version>1.8.6</version></dependency><dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version><scope>compile</scope></dependency>

yml

server:port: 9528spring:application:name: cloudalibaba-sentinel-gateway     # sentinel+gataway整合Casecloud:nacos:discovery:server-addr: localhost:8848gateway:routes:- id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名uri: http://localhost:9001                #匹配后提供服务的路由地址predicates:- Path=/pay/**                      # 断言,路径相匹配的进行路由

启动类

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

config类

@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(-1)public GlobalFilter sentinelGatewayFilter() {return new SentinelGatewayFilter();}@PostConstruct //javax.annotation.PostConstructpublic void doInit() {initBlockHandler();}//处理/自定义返回的例外信息private void initBlockHandler() {Set<GatewayFlowRule> rules = new HashSet<>();rules.add(new GatewayFlowRule("pay_routh1").setCount(2).setIntervalSec(1));GatewayRuleManager.loadRules(rules);BlockRequestHandler handler = new BlockRequestHandler() {@Overridepublic Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {Map<String,String> map = new HashMap<>();map.put("errorCode", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());map.put("errorMessage", "请求太过频繁,系统忙不过来,触发限流(sentinel+gataway整合Case)");return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(map));}};GatewayCallbackManager.setBlockHandler(handler);}}

config参考官网配置
在这里插入图片描述
测试:
启动9001服务、9528网关,访问http://localhost:9001/pay/nacos/333成功,访问http://localhost:9528/pay/nacos/333也成功,如果http://localhost:9528/pay/nacos/333访问并发起来了,就会提示【请求太过频繁,系统忙不过来,触发限流(sentinel+gataway整合Case)】

相关文献

Sentinel github:https://github.com/alibaba/Sentinel
Sentinel官网:https://sentinelguard.io/zh-cn/docs/introduction.html

就先说到这 \color{#008B8B}{ 就先说到这} 就先说到这
在下 A p o l l o \color{#008B8B}{在下Apollo} 在下Apollo
一个爱分享 J a v a 、生活的小人物, \color{#008B8B}{一个爱分享Java、生活的小人物,} 一个爱分享Java、生活的小人物,
咱们来日方长,有缘江湖再见,告辞! \color{#008B8B}{咱们来日方长,有缘江湖再见,告辞!} 咱们来日方长,有缘江湖再见,告辞!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/368363.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【多媒体】富客户端应用程序GUI框架 JavaFX 2.0 简介

JavaFX 最初是由 Oracle 推出的一个用于开发富客户端应用程序的框架&#xff0c;它提供了丰富的用户界面控件、布局容器、3D图形绘制、媒体播放和动画等功能&#xff0c;旨在取代较旧的 Swing 框架。JavaFX 于 2007 年推出&#xff0c;2011 年 10 月发布了2.0 版本。JavaFX 2.0…

数据结构(一)C语言补

数据结构 内存空间划分 一个进程启动后&#xff0c;会生成4G的内存空间 0~3G是用户空间(应用层) 3~4G是内核空间(底层) 0~3G 3~4G 所有的进程都会共享3G~4G的内核空间&#xff0c; 但是每个进程会独立拥有0~3G的用户空间。 栈区 存放数据特点 栈区存放数据的申请空间的先后…

小学vr虚拟课堂教学课件开发打造信息化教学典范

在信息技术的浪潮中&#xff0c;VR技术正以其独特的魅力与课堂教学深度融合&#xff0c;引领着教育方式的创新与教学方法的变革。这一变革不仅推动了“以教促学”的传统模式向“自主探索”的新型学习方式转变&#xff0c;更为学生带来了全新的学习体验。 运用信息技术融合VR教学…

C++那些事之小项目实战-进程间通信

小项目实战之进程间通信 进程间通信是一个非常重要的话题&#xff0c;特别是像一些大型项目都有它的影子&#xff0c;例如&#xff1a;PostgreSQL使用了管道完成copy的进程间通信&#xff0c;那么本节也将基于这个主题&#xff0c;使用C去搭建一个进程间通过管道通信的demo出来…

【代码随想录】【算法训练营】【第53天】 [739]每日温度 [496]下一个更大元素I [503]下一个更大元素II

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 48&#xff0c;周六&#xff0c;不能再坚持~ 题目详情 [739] 每日温度 题目描述 739 每日温度 解题思路 前提&#xff1a;寻找任一个元素的右边比自己大的元素的位置 思路&#xff1a;通常…

(三十一)Flask之wtforms库【剖析源码下篇】

每篇前言&#xff1a; &#x1f3c6;&#x1f3c6;作者介绍&#xff1a;【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者 &#x1f525;&#x1f525;本文已收录于Flask框架从入门到实战专栏&#xff1a;《Flask框架从入…

spring boot初始化的几个总结

spring intializr File->New->Project 注意&#xff1a;Spring Initializer中 Java版本选择模块已经不支持1.8了。 Spring Boot 3.x要求 Java最低版本为17&#xff0c; 最新的SpringBoot版本已经要求Java22了 所以&#xff0c;你可以升级Java版本&#xff0c;使用Spri…

6.26.3 基于Transformer的深度神经网络在数字乳腺断层合成图像上的乳腺癌分类

开发一种有效的深度神经网络模型&#xff0c;该模型结合了相邻图像部分的上下文&#xff0c;以检测数字乳腺断层合成(DBT)图像上的乳腺癌。 数字乳房断层合成(DBT)是一种医学成像技术&#xff0c;其中检测器围绕患者以有限角度旋转并记录多幅图像。然后将这些图像重建为二维(2D…

【Python】变量与基本数据类型

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️Python】 文章目录 前言变量声明变量变量的命名规则 变量赋值多个变量赋值 标准数据类型变量的使用方式存储和访问数据&#xff1a;参与逻辑运算和数学运算在函数间传递数据构建复杂的数据结构 NameE…

关于5G和卫星

手机&#xff0c;已经串联起了我们生活中的一切环节。我们随时随地拿出手机&#xff0c;都能畅快地上网。 这一切是如此地理所当然&#xff0c;以至于我们甚至想不到这样不可思议的问题&#xff1a; 移动通信网络真的无处不在吗&#xff1f; 我们都知道&#xff0c;地球虽叫…

WordPress主题大前端DUX v8.7源码下载

全新&#xff1a;用户注册流程&#xff0c;验证邮箱&#xff0c;设置密码 新增&#xff1a;列表显示小视频和横幅视频 新增&#xff1a;文章内容中的外链全部增加 nofollow 新增&#xff1a;客服功能中的链接添加 nofollow 优化&#xff1a;产品分类的价格显示

Day05-讲师列表前端-讲师信息添加

代码&#xff1a; //添加讲师 addTeacher(teacher){ return request({ url:/eduservice/teacher/addTeacher, method:‘post’, data:teacher }) } &#xff08;2&#xff09;在页面实现调用 代码&#xff1a; 讲师添加 <el-button type“primary” :disabled“sav…

【Qt之·类QVariant·数据类型】

系列文章目录 文章目录 前言一、概述二、操作及用法1.1 存储数据1.2 获取数据1.3 设置数据1.4 数据类型判断1.5 判断数据是否有效 三、实例演示总结 前言 QVariant是Qt开发中非常重要的一部分&#xff0c;它是Qt的一个核心类&#xff0c;用于处理不同数据类型之间的转换和传递。…

Elasticsearch集群部署(下)

目录 上篇&#xff1a;Elasticsearch集群部署&#xff08;上&#xff09;-CSDN博客 七. Filebeat 部署 八. 部署Kafka 九. 集群测试 链接&#xff1a;https://pan.baidu.com/s/1AFXSmDdY5xBb7g35ipKoaw?pwdfa9m 提取码&#xff1a;fa9m 七. Filebeat 部署 为什么用 F…

使用 draw.io 画图

尽管我非常喜欢 wps 和 office 的 ppt 画图&#xff0c;但因为它们对数学公式的糟糕支持&#xff0c;我不得不另外寻找一个画图工具。当然我也同样很喜欢 visio &#xff0c;但同样的&#xff0c;它对数学公式的支持糟糕&#xff0c;另外&#xff0c;最为重要的是&#xff0c;v…

java设计模式(十二)享元模式(Flyweight Pattern)

1、模式介绍&#xff1a; 享元模式是一种结构型设计模式&#xff0c;旨在通过共享对象来有效支持大量细粒度的对象。它通过将对象的状态分为内部状态&#xff08;可共享&#xff09;和外部状态&#xff08;不可共享&#xff09;来减少内存消耗和提高性能。内部状态存储在享元对…

​Chrome插件:React Developer Tools为React开发调试而生

React Developer Tools 是什么? 它是允许在Chrome和Firefox开发者工具中检查React组件层次结构的扩展插件。 插件源码下载 源码下载地址:GitHub - facebook/react-devtools at v3 下载完成以后执行红框中的代码,下载react-devtools 源码,源码如下图所示: 插件打包 当前n…

【软件测试】如何搭建appium工具环境?

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、安装Java Development Kit&#xff08;JDK&#xff09; 前往Oracle官网下载JDK。 在https:/…

用Python制作动态钟表:实时显示时间的动画

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame绘制钟表函数主循环 完整代码 引言 动态钟表是一种直观且实用的UI元素&#xff0c;能够实时显示当前时间。在这篇博客中&#xff0c;我们将使用Python创建一个动态钟表&#xff0c;通过利用Pygame库来实…

高校搭建AIGC新媒体实验室,创新新闻教育教学模式

高校作为人才培养的重要阵地&#xff0c;必须紧跟时代步伐&#xff0c;不断创新教育教学模式&#xff0c;提升跨界融合育人水平&#xff0c;通过AIGC新媒体实验室探索创新人才培养模式。AIGC新媒体实验室不仅能够高效赋能高校宣传媒体矩阵&#xff0c;也可以助力教学实践与AIGC…