掌握sentinel的概念
学会使用Sentinel做限流
什么是雪崩问题:微服务中,服务间调用关系错综复杂,一个微服务往往依赖于多个其它微服务。如果服务提供者A发生了故障,当前的应用的部分业务因为依赖于服务A,因此也会被阻塞。此时,其它不依赖于服务A的业务似乎不受影响。但是,依赖服务A的业务请求被阻塞,用户不会得到响应,则tomcat的这个线程不会释放,于是越来越多的用户请求到来,越来越多的线程会阻塞.服务器支持的线程和并发数有限,请求一直阻塞,会导致服务器资源耗尽,从而导致所有其它服务都不可用,那么当前服务也就不可用了。
那么,依赖于当前服务的其它服务随着时间的推移,最终也都会变的不可用,形成级联失败,雪崩就发生了。
解决雪崩问题的常见方式有四种:
超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待
线程隔离(仓壁模式):船舱都会被隔板分离为多个独立空间,当船体破损时,只会导致部分空间进入,将故障控制在一定范围内,避免整个船体都被淹没。于此类似,我们可以限定每个业务能使用的线程数,避免耗尽整个tomcat的资源,因此也叫线程隔离。
熔断器(断路器):断路器模式:由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。断路器会统计访问某个服务的请求数量,异常比例,当发现访问服务D的请求异常比例过高时,认为服务D有导致雪崩的风险,会拦截访问服务D的一切请求,形成熔断。
限流:也就是流量控制限制业务访问的QPS,避免服务因流量的突增而故障。
总结
什么是雪崩问题?
微服务之间相互调用,因为调用链中的一个服务故障,引起整个链路都无法访问的情况。
可以认为:
1、限流是对服务的保护,避免因瞬间高并发流量而导致服务故障,进而避免雪崩。是一种预防措施。
2、超时处理、线程隔离、降级熔断是在部分服务故障时,将故障控制在一定范围,避免雪崩。是一种补救措施。
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整
流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:
流量控制有以下几个角度:
Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
除了流量控制以外,及时对调用链路中的不稳定因素进行熔断也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,可能会导致请求发生堆积,进而导致级联错误。
Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。
在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。
Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配。
Sentinel 对这个问题采取了两种手段:
和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
•完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
https://github.com/alibaba/Sentinel/releases/tag/v1.8.0
下载后可以直接运行
java -jar sentinel-dashboard-1.8.0.jar
看到如下页面说明启动成功,默认端口8080
浏览器访问http://localhost:8080/看到sentinel登录页面
输入用户名:sentinel,密码:sentinel 登录系统
看到如下页面,左侧属性菜单是空的,默认sentinel是懒加载的,因为没有任何流量访问,所以不会有菜单显示出来,要想看到控制台中的内容,我们需要搭建一个应用程序,访问一下才能有菜单显示出来
如果要修改Sentinel的默认端口、账户、密码,可以通过下列配置:
pom.xml文件增加如下依赖
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>com.summergroupId>
<artifactId>alibaba-serverartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>provider-serviceartifactId>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<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>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-loadbalancerartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bootstrapartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
dependencies>
project>
编写启动类
@SpringBootApplication
@EnableDiscoveryClient // 开启注册中心客户端
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}
}
编写一个controller类
package com.summer.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author Echo
* @version 1.0
* @date 2023/8/31 19:08
*/
@RequestMapping("/order")
public class OrderController {
@GetMapping("/query/{id}")
public String queryOrder(@PathVariable("id") Long id) {
return "查询订单成功->id为:" + id;
}
@GetMapping("/update/{id}")
public String updateOrder(@PathVariable("id") Long id) {
return "更新订单成功id为:" + id;
}
}
bootstrap.yml中增加如下内容
server:
port: 8081
spring:
application:
name: provider-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #注册中心地址
namespace: 7c1747ff-bc39-4903-b6bb-a3eefd5fdabc # 命名空间
group: DEV_GROUP # 群组
config:
server-addr: localhost:8848 #配置中心地址
group: DEV_GROUP # 群组
namespace: 7c1747ff-bc39-4903-b6bb-a3eefd5fdabc # 命名空间
file-extension: yml # 配置文件拓展名
sentinel:
transport:
dashboard: localhost:8080
访问provider-service的任意端点
启动程序打开浏览器并访问一下接口http://localhost:8081/order/query/1,这样才能触发sentinel的监控
然后再访问sentinel的控制台,查看效果:就会发现provider-service这个微服务名称已经出现在左侧树形菜单中。
当我们访问应用程序的接口时候,比如连续访问http://localhost:8081/order/query/1,这个页面上会显示出曲线图以及统计表格。
簇点链路
当请求进入微服务时,首先会访问DispatcherServlet,然后进入Controller、Service、Mapper,这样的一个调用链就叫做簇点链路。簇点链路中被监控的每一个接口就是一个资源。
默认情况下sentinel会监控每一个端点(controller中的方法),因此每一个端点就是调用链路中的一个资源。
每一个可以访问的资源都列在这里,并且表格中也可以看出一些统计信息。操作一栏中流控、降级、热点、授权这四个按钮跟左侧树形菜单中的流控规则,降级规则,热点规则,授权规则点击进入的是相同功能。
这几个功能也是我们重点要演示的。
点击order/query/1右侧的流控按钮可以看到如下流控配置页面,表单中可以填写限流规则
演示1:Sentinel限流-流控模式-直接模式: 统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
流控规则: 其含义是限制 order/query/1这个资源的单机QPS为2,即每秒只允许2次请求,超出的请求会被拦截并报错。
我们点击新增按钮,添加这条流控规则.
我们通过浏览器访问order/query/1这个接口,使用鼠标快速点击,当点击过快的时候,超过了QPS=2,那么会看到Sentinel做出了限流响应。
演示2: Sentinel限流-流控模式-关联模式: 统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
流控规则: 当/order/update/{id}资源被访问的QPS超过5时,对/order/query/{id}请求限流
使用场景: 比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是优先支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。
对哪个端点限流,就点击哪个端点后面的按钮。我们是对订单查询/order/query{id}限流,因此点击它后面的按钮:
Jmeter测试
我们请求的目标是/order/update/1,这样这个断点就会触发阈值。但限流的目标是/order/query/1,我们在浏览器访问,会出现如下被限流的返回信息,确实被限流了。
演示3:Sentinel限流-流控模式-链路模式: 统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流.
流控规则: 限制queryGoods的QPS为2,只限制从/order/query进入的请求
使用场景: 有查询订单和创建订单业务,两者都需要查询商品。针对从查询订单进入到查询商品的请求统计,并设置限流。
默认情况下,OrderService中的方法是不被Sentinel监控的,需要我们自己通过注解来标记要监控的方法。
给OrderService的queryGoods方法添加@SentinelResource注解:
package com.summer.controller;
import com.summer.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Echo
* @version 1.0
* @date 2023/8/31 19:08
*/
@RestController
@RefreshScope
@RequestMapping("/order")
public class OrderController {
@Autowired
OrderService orderService;
/**
* 查询订单
* @return
*/
@GetMapping("/query")
public String queryOrder() {
// 商品查询
orderService.queryGoods();
return orderService.queryOrder();
}
/**
* 更新订单
* @return
*/
@GetMapping("/update")
public String updateOrder() {
return orderService.updateOrder();
}
/**
* 新增订单
* @return
*/
@GetMapping("/create")
public String createOrder() {
// 商品查询
orderService.queryGoods();
return orderService.createOrder();
}
/**
* 查询商品
* @return
*/
@GetMapping("/queryGoods")
public String queryGoods() {
return orderService.queryGoods();
}
}
@SentinelResource("goods")
public String queryGoods() {
return "查询商品成功";
}
链路模式中,是对不同来源的两个链路做监控。但是sentinel默认会给进入SpringMVC的所有请求设置同一个root资源,会导致链路模式失效。
添加流控规则: 只统计从/order/query进入/goods的资源,QPS阈值为1,超出则被限流。
可以看到这里200个用户,50秒内发完,QPS为4,超过了我们设定的阈值1.
每次只有1个通过
流控效果是指请求达到流控阈值时应该采取的措
演示4:Sentinel限流-流控效果-warm up: 预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。
warm up说明: 阈值一般是一个微服务能承担的最大QPS,但是一个服务刚刚启动时,一切资源尚未初始化(冷启动),如果直接将QPS跑到最大值,可能导致服务瞬间宕机。warm up也叫预热模式,是应对服务冷启动的一种方案。请求阈值初始值是 maxThreshold / coldFactor,持续指定时长后,逐渐提高到maxThreshold值。而coldFactor的默认值是3.
例如,我设置QPS的maxThreshold为10,预热时间为5秒,那么初始阈值就是 10 / 3 ,也就是3,然后在5秒后逐渐增长到10.
刚启动时,大部分请求失败,成功的只有3个,说明QPS被限定在3.
随着时间推移,成功率越来越高.
到Sentinel控制台查看/order/query的实时监控
一段时间后,发现通过的QPS变多了
演示5:Sentinel限流-流控效果-排队等待: 让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长
排队等待说明: 当请求超过QPS阈值时,快速失败和warm up 会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
QPS为15,已经超过了我们设定的10。
如果是之前的 快速失败、warmup模式,超出的请求应该会直接报错。但是我们看看运行结果,全部都通过了.
再去sentinel查看实时监控的QPS曲线,非常平滑,一致保持在10,但是超出的请求没有被拒绝,而是放入队列。因此响应时间(等待时间)会越来越长。
当队列满了以后,才会有部分请求失败
演示6:Sentinel限流-热点参数限流-全局参数:之前的限流是统计访问某个资源的所有请求,判断是否超过QPS阈值。而热点参数限流是分别统计参数值相同的请求,判断是否超过QPS阈值。
全局参数限流:例如,一个根据id查询商品的接口,访问/order/query/goods/{id}的请求中,id参数值会有变化,热点参数限流会根据参数值分别统计QPS.假如我们限制id=1的请求触发阈值被限流,那么id值不为1的请求不受影响。
我们配置的QPS=10触发阈值,从运行结果看出超过5个被限流了.
热点参数限流说明: 刚才的配置中,对查询商品这个接口的所有商品一视同仁,QPS都限定为5.而在实际开发中,可能部分商品是热点商品,例如双11或者618秒杀商品,我们希望这部分商品的QPS限制与其它商品不一样,高一些。那就需要配置热点参数限流的高级选项进行控制了.
结合上一个配置,这里的含义是对0号的int类型参数限流,每1秒相同参数的QPS不能超过5,但是有两个例外:
•如果参数值是6,则每1秒允许的QPS为2.
•如果参数值是8,则每1秒允许的QPS为3.
SpringCloud中,微服务调用都是通过Feign来实现的,因此做客户端保护必须整合Feign和Sentinel。
修改配置,开启sentinel功能
feign:
sentinel:
enabled: true # 开启feign对sentinel的支持
对应服务配置限流规则
我们直接访问服务被限流后返回的是sentinel的默认异常.
通过feign调用被限流后返回fallback的配置信息.
线程数:是该资源能使用用的tomcat线程数的最大值。也就是通过限制线程数量,实现线程隔离(舱壁模式)。
使用场景: 给 order-service服务中的feign调用根据商品名称查询接口/order/queryGoodsByName设置流控规则,线程数不能超过 2,然后利用jemeter测试。
0s内我们发送40个请求,有较大概率并发线程数超过2,而超出的请求会走之前定义的失败降级逻辑。
当Jmeter访问接口的时,发现虽然结果都是通过了,不过部分请求得到的响应是降级返回fallback信息.
熔断降级说明:熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。
SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0]
,代表 0% - 100%。ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。字段 | 说明 | 默认值 |
---|---|---|
resource | 资源名,即规则的作用对象 | |
grade | 熔断策略,支持慢调用比例/异常比例/异常数策略 | 慢调用比例 |
count | 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 | |
timeWindow | 熔断时长,单位为 s | |
minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) | 5 |
statIntervalMs | 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) | 1000 ms |
slowRatioThreshold | 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) |
我们给OrderController的查询订单详情接口设置降级规则,慢调用的RT阈值为500ms,最小请求数量为5,失败阈值比例为0.5,熔断时长为5.
解读:RT超过50ms的调用是慢调用,统计请求,如果请求量超过5次,并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。
通过休眠模拟一个延迟时间
/**
* controller
* 查询订单
* @return
*/
@GetMapping("/queryOrderDetail")
public String queryOrderDetail(Integer id) throws InterruptedException {
return orderService.queryOrderDetail(id);
}
// service
public String queryOrderDetail(Integer id) throws InterruptedException {
if(id==1){
Thread.sleep(60);
}
return "查询订单详情成功";
}
导致id=3的也被熔断了
异常比例或异常数说明:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。
统计如果请求量超过5次,并且异常比例不低于0.5,则触发熔断.
统计如果请求量超过5次,并且异常比例不低于2次,则触发熔断.
授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
Sentinel提供了 RequestOriginParser 接口来处理访问来源,Sentinel保护的资源如果被访问,就会调用 RequestOriginParser解析访问来源。
public interface RequestOriginParser {
/**
* 从请求request对象中获取origin,获取方式自定义
*/
String parseOrigin(HttpServletRequest request);
}
这个方法的作用就是从request对象中,获取请求者的origin值并返回。默认情况下,sentinel不管请求者从哪里来,返回值永远是default,也就是说一切请求的来源都被认为是一样的值default。因此,我们需要自定义这个接口的实现,让不同的请求,返回不同的origin。
/**
* @author Echo
* @version 1.0
* @date 2023/9/1 19:42
*/
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
@Component
public class HeaderOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
// 1.获取请求头
String origin = request.getHeader("origin");
// 2.非空判断
if (StringUtils.isEmpty(origin)) {
origin = "blank";
}
return origin;
}
}
server:
port: 6060
spring:
application:
name: gateway-service
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: localhost:8848 #注册中心地址
namespace: 7c1747ff-bc39-4903-b6bb-a3eefd5fdabc # 命名空间
group: DEV_GROUP # 群组
config:
server-addr: localhost:8848 #配置中心地址
group: DEV_GROUP # 群组
namespace: 7c1747ff-bc39-4903-b6bb-a3eefd5fdabc # 命名空间
file-extension: yml # 配置文件拓展名
gateway:
default-filters:
- AddRequestHeader=origin,gateway #给网关添加请求头
routes:
- id: consumer-service
uri: lb://consumer-service
predicates:
- Query=green
- Path=/gateway/consumer/**
filters:
- StripPrefix=1
- id: provider-service
uri: lb://provider-service
predicates:
- Path=/gateway/provider/**,/gateway/order/**
# - Query=red # 我们的请求参数里带有red的请求 且 路径能匹配到/provider/ 就转发到http://localhost:8080
filters:
- StripPrefix=1
request:
whites:
- /gateway/consumer/**
- /gateway/feign/**
- /gateway/order/**
在OrderController中新增测试授权接口/testAuthorize
/**
* 测试授权
* @return
*/
@GetMapping("/testAuthorize")
public String testAuthorize() {
return "授权通过";
}
添加授权规则
直接访问此时已经访问不通了,说明被授权规则限制.
反之若把gateway设置为黑名单,则网关访问不通过,直接访问服务通过.
如果要自定义异常时的返回结果,需要实现BlockExceptionHandler接口
public interface BlockExceptionHandler {
/**
* 处理请求被限流、降级、授权拦截时抛出的异常:BlockException
*/
void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
}
这个方法有三个参数:
BlockException包含多个不同的子类:
自定义异常处理
我们在provider-service里自定义一个sentinel的异常处理
package com.summer.exception;
/**
* @author Echo
* @version 1.0
* @date 2023/9/1 20:11
*/
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
String msg = "未知异常";
int status = 429;
if (e instanceof FlowException) {
msg = "请求被限流";
} else if (e instanceof ParamFlowException) {
msg = "请求被热点参数限流";
} else if (e instanceof DegradeException) {
msg = "请求被降级";
} else if (e instanceof AuthorityException) {
status = 401;
msg = "无权限访问";
}
response.setContentType("application/json;charset=utf-8");
response.setStatus(status);
response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");
}
}
为什么要使用集群流控呢?假设我们希望给某个用户限制调用某个 API 的总 QPS 为 50,但机器数可能很多(比如有 100 台)。这时候我们很自然地就想到,找一个 server 来专门来统计总的调用量,其它的实例都与这台 server 通信来判断是否可以调用。这就是最基础的集群流控的方式。
另外集群流控还可以解决流量不均匀导致总体限流效果不佳的问题。假设集群中有 10 台机器,我们给每台机器设置单机限流阈值为 10 QPS,理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。
集群流控中共有两种身份:
Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
再搭建一个跟provider-service一样的服务,并且注册到nacos中,这样两个provider-service就组成一个集群。在Sentinel中可以看到目前我们provider-service有两个实例
添加一个Token Server
添加Token Server ,Token Client,设置集群QPS
1、资源名指的就是需要进行流量限制的接口名
2、针对来源是可以对调用该接口的来源进行限流配置,默认default全部
3、阈值类型有 qps和并发线程数,我们这里选qps,集群阈值填1,表示1s只能有一个请求,集群阈值模式可以选单机均摊和总体阈值,单机均摊会根据服务的总连接数计算总的阈值,而总体阈值是针对于整个集群而言,这里我们选总体阈值,失败退化是指token服务不可用之后自动转换成单机限流。
配置好规则后,通过浏览器访问 http://localhost:6060/gateway/order/testCollect 这个是provider-service的接口
当访问速度很慢的时候,可以正常返回
如果快速点击刷新按钮,会报错,看到限流效果.
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN
),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
系统规则支持以下的模式:
maxQps * minRt
估算得出。设定参考值一般是 CPU cores * 2.5
。我们的provider-service有两个微服务实例,目前可以在Sentinel控制台看到,点击机器列表菜单,可以在列表中看到我们两个实例的列表