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.mzph.cn/web/39471.shtml

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

相关文章

Django 安装 Zinnia 后出现故障

在Django中安装和配置Zinnia时遇到故障可能有多种原因&#xff0c;通常包括版本兼容性、依赖关系或配置问题。这里提供一些常见的解决方法和调试步骤&#xff0c;帮助大家解决问题。 首先&#xff0c;确保您安装的Zinnia版本与Django版本兼容。查看Zinnia的官方文档或GitHub页…

Linux库概念及相关编程(动态库-静态库)

Linux库概念及相关编程 分文件编程案例 分文件编程是指将程序按功能模块划分成不同的文件进行编写&#xff0c;这种方法有以下好处&#xff1a; 功能责任划分&#xff1a;每个文件对应一个功能模块&#xff0c;职责明确&#xff0c;易于理解和维护。方便调试&#xff1a;可以…

C++左值/右值/左值引用/右值引用

1&#xff09;C入门级小知识&#xff0c;分享给将要学习或者正在学习C开发的同学。 2&#xff09;内容属于原创&#xff0c;若转载&#xff0c;请说明出处。 3&#xff09;提供相关问题有偿答疑和支持。 左值和右值的概念&#xff1a; 早期的c语言中关于左值和右值的定义&a…

每日一题——Python实现PAT乙级1026 程序运行时间(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 代码结构和逻辑 时间复杂度 空间复杂度 代码优化建议 总结 我要更强 …

交换机需要多大 buffer

有点违背直觉&#xff0c;但是真事儿&#xff0c;交换机过境的流越多&#xff0c;所需 buffer 越小&#xff0c;这是为什么&#xff1f; 范氏(范雅各布森&#xff0c;van jacobson)管道的 aimd 流建议 buffer_size 为 bdp&#xff0c;这很容易理解&#xff0c;因为 aimd 流最小…

币界网讯,预计以太坊现货 ETF 将于 7 月中旬推出

刚刚 ETF Store 总裁 Nate Geraci 在 X &#xff08;前Twitter&#xff09;平台上宣布&#xff0c;备受数字货币市场期待的SEC以太坊现货 ETF提案&#xff0c;将于7 月中旬通过美国证券交易委员会&#xff08;SEC&#xff09;批准。Nate Geraci透露修订后的 S-1 文件将于 7 月 …

pnpm的坑

请问pnpm的两个坑怎么解决&#xff1a; 第一个坑&#xff1a;没有节省磁盘空间 我已经配置了依赖的存储位置&#xff0c; 但我在项目里pnpm install以后&#xff0c;发现依赖包还是很大&#xff0c; 然后发现里面的链接并不是指向先前配置的依赖存储位置&#xff0c;而是指…

【数智化人物展】袋鼠云CEO宁海元:大模型时代,Data+AI将成为新的基础设施

宁海元 本文由袋鼠云CEO宁海元投递并参与由数据猿联合上海大数据联盟共同推出的《2024中国数智化转型升级先锋人物》榜单/奖项评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 身处这个瞬息万变的数字经济时代&#xff0c;传统的生产模式往往依赖于经验和固定的流程&…

k8s-第六节-数据持久化

数据持久化 kubernetes 集群不会为你处理数据的存储&#xff0c;需要为数据库挂载一个磁盘来确保数据的安全。 可以选择云存储、本地磁盘、NFS。 本地磁盘&#xff1a;可以挂载某个节点上的目录&#xff0c;但是这需要限定 pod 在这个节点上运行 云存储&#xff1a;不限定节…

GEE计算遥感生态指数RESI

目录 RESI湿度绿度热度干度源代码归一化函数代码解释整体的代码功能解释:导出RSEI计算结果参考文献RESI RSEI = f (Greenness,Wetness,Heat,Dryness)其遥感定义为: RSEI = f (VI,Wet,LST,SI)式中:Greenness 为绿度;Wetness 为湿度;Thermal为热度;Dryness 为干度;VI 为植被指数…

手写starter核心思路流程-全网最详细版本

全网最详细手写starter组件教程 那么在写这篇博客之前,先问一下大家为什么要写starter组件,仅仅只是为了炫技吗?还是真正的在业务中需要.在现在的开发环境下,什么是竞争力? 举例分页查询来说,每个来公司的程序员都有一套自己写分页的流程,但是这套流程基本上都是重复的,那么…

Docker学习笔记(一)概念理解

一、什么是docker容器 Docker容器是一种轻量级、可移植的软件封装技术&#xff0c;它允许开发者将应用程序及其依赖、配置文件、运行环境等打包到一个独立的、自包含的执行单元中。容器与虚拟机相似&#xff0c;都提供了隔离的运行环境&#xff0c;但容器更加轻量级&#xff0c…

如何清理电脑内存?让电脑运行如飞!

电脑内存&#xff08;RAM&#xff09;的清理对于维持系统的流畅运行至关重要。随着使用时间的增加&#xff0c;系统内存会被各种应用程序和后台进程占用&#xff0c;导致系统响应变慢&#xff0c;甚至出现卡顿现象。通过有效地清理内存&#xff0c;可以提升电脑的性能&#xff…

深入理解如何撤销 Git 中不想提交的文件

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

MySQL内存使用率高且不释放问题排查与总结

背景 生产环境mysql 5.7内存占用超过90%以上&#xff0c;且一直下不来。截图如下&#xff1a; 原因分析 1、确定mysql具体的占用内存大小&#xff0c;通过命令&#xff1a;cat /proc/Mysql进程ID/status查看 命令执行后的结果比较多&#xff08;其他参数的含义想了解可参考这…

静态路由的配置

5.3静态路由 静态路由由网络管理员手动配置&#xff0c;配置方便&#xff0c;对系统要求低&#xff0c;适用于拓扑结构简单并且稳定的小型网络。缺点是不能自动适应网络拓扑的变化&#xff0c;需要人工干预。 5.3.1静态路由实验 1、实验需求 ① 掌握路由表的概念&#xff1…

cpp随笔——如何实现一个简单的进程心跳功能

什么是进程的心跳 在我们日常后台服务程序运行中,一般是调度模块&#xff0c;进程心跳以及进程监控共同工作&#xff0c;进而实现实现服务的稳定运行,在前面我们介绍过如何去实现一个简单的调度模块,而今天我们所要介绍的就是如何实现进程的心跳&#xff0c;首先什么是进程的心…

git上传文件

git init git add . git commit -m " " git remote add origin 仓库的地址 git push -u origin master 如果出现以下问题 可以用这一句强制上传 git push -f origin master

Centos下rpm和yum执行卡住问题(已解决)

问题描述 执行rpm和yum卡住&#xff0c; 没有任何报错信息&#xff0c;且无法 ctrl c 终止&#xff0c;只能通过后台 kill -9 杀死。 问题排查&#xff1a; 查看yum日志&#xff1a;yum -vv 软件包 会发现卡在 loading keyring from rpmdb&#xff0c;即load DB存在问题。 …

使用 llamaIndex 快速实现智能体

AI 智能体就是可以根据当前环境进行推理&#xff0c;并根据处理结果进行下一步的操作。简单来说 AI 智能体可以与外界环境进行交互&#xff0c;并根据结果执行更复杂的操作。本文将通过llamaIndex 实现一个简单的 Agent 实时获取数据&#xff0c;由于大模型是通过静态数据进行训…