sentinel(微服务限流)
官网地址:https://sentinelguard.io/zh-cn/
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是由阿里巴巴开源的一款流量防护组件,Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
微服务的雪崩效应
概述:在微服务系统架构中,服务间调用关系错综复杂,一个微服务往往依赖于多个其它微服务。一个服务的不可用导致整个系统的不可用的现象就被称之为雪崩效应。
当服务D出现了问题了以后,调用服务D的服务A的线程就得不到及时的释放,在高并发情况下,随着时间的不断推移服务A的系统资源会被线程耗尽,最终导致服务A出现了问题,同理就会导致其他的服务也不能进行访问了。
解决方案
超时处理
openfeign超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待
隔离处理
隔离处理:将错误隔离在可控的范围之内,不要让其影响到其他的程序的运行。
熔断处理
熔断处理:由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。
流量控制
流量控制:限制业务访问的QPS(每秒的请求数),避免服务因流量的突增而故障。
限流是一种预防措施,避免因瞬间高并发流量而导致服务故障,进而避免雪崩。其他的处理方式是一种补救措施,在部分服务故障时,将故障控制在一定范围,避免雪崩。
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
sentinel入门
sentinel管理后台下载地址:https://github.com/alibaba/Sentinel/releases
下载完毕以后就会得到一个jar包
启动sentinel
- 将jar包放到任意非中文目录,执行命令:
java -jar sentinel-dashboard-2.0.0-alpha-preview.jar
- 如果要修改Sentinel的默认端口、账户、密码,可以通过下列配置:
配置项 | 默认值 | 说明 |
---|---|---|
server.port | 8080 | 服务端口 |
sentinel.dashboard.auth.username | sentinel | 默认用户名 |
sentinel.dashboard.auth.password | sentinel | 默认密码 |
- 例如,修改端口:
java -Dserver.port=8090 -jar sentinel-dashboard-2.0.0-alpha-preview.jar
默认访问http://localhost:8080页面,就可以看到sentinel的控制台了
java整合sentinel
1、引入sentinel依赖
<!--sentinel-->
<dependency><groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2、配置控制台
修改application.yaml文件,添加下面内容
spring:cloud:sentinel:transport:dashboard: localhost:8080 # 配置sentinel控制台地址
3、访问spzx-cloud-user的任意接口
打开浏览器,访问http://localhost:10100/api/user/findUserByUserId/1,这样才能触发sentinel的监控。然后再访问sentinel的控制台,查看效果:
流量控制
雪崩问题虽然有四种方案,但是限流是避免服务因突发的流量而发生故障,是对微服务雪崩问题的预防。
相关概念
簇点链路:当请求进入微服务时,首先会访问DispatcherServlet,然后进入Controller、Service、Mapper,这样的一个调用链就叫做簇点链路。
资源:簇点链路中被监控的每一个接口就是一个资源,流控、熔断等都是针对簇点链路中的资源来设置的。
默认情况下sentinel会监控spring mvc的每一个端点(Endpoint,也就是controller中的方法),因此spring mvc的每一个端点就是调用链路中的一个资源。
我们可以点击对应资源后面的按钮来设置规则:
1、流控:流量控制
2、降级:降级熔断
3、热点:热点参数限流,是限流的一种
4、授权:请求的权限控制
快速入门
需求:给 /api/user/findUserByUserId/{userId}这个资源设置流控规则,QPS不能超过 5,然后测试。
步骤:
1、首先在sentinel控制台添加限流规则
2、利用jmeter测试(模拟并发请求)
Apache JMeter 是 Apache 组织基于 Java 开发的压力测试工具,用于对软件做压力测试。
下载地址:https://archive.apache.org/dist/jmeter/binaries/
通过如下命令打开jmeter
java -jar ApacheJMeter.jar
1、新建线程组
2、配置参数==>10个线程,1秒内运行完,QPS是10,超过了5。
3、添加HTTP请求:
4、配置HTTP请求的参数:
5、添加查看结果树,查看请求执行结果
6、运行查看
注意:如果测试结果不是上述情况,那是因为sentinel在统计请求的时候,把一部分的请求统计到了下一秒中导致的。
流控模式
在添加限流规则时,点击高级选项,可以选择三种流控模式:
1、直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
2、关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
3、链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流
关联模式
关联模式:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
语法说明:对/api/user/updateUserById资源的请求进行统计,当访问流量超过阈值时,就对/api/user/findUserByUserId/{userId}进行限流,避免影响/api/user/updateUserById资源。
使用场景:比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是优先支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。
案例实现:
1、在UserController新建一个端点:/api/user/updateUserById,无需实现业务
// 修改用户数据端点
@GetMapping(value = "/updateUserById")
public String updateUserById() {return "修改用户数据成功";
}
2、重启服务,访问对应的端点,让其产生簇点链路
3、配置流控规则,当/api/user/updateUserById资源被访问的QPS超过5时,对/api/user/findUserByUserId/1请求限流。对哪个端点限流,就点击哪个端点后面的按钮。我们是对用户查询/api/user/findUserByUserId/1限流,因此点击它后面的按钮:
我们在浏览器访问,可以发现:
确实被限流了。
链路模式
链路模式:只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值,如果超过阈值对从该链路请求进行限流。
配置方式:
1、/api/user/save --> users
2、/api/user/query --> users
如果只希望统计从/api/user/query进入到users的请求,并进行限流操作,则可以这样配置:
案例实现:
1、在UserService中添加一个queryUsers方法,不用实现业务
public void queryUsers(){System.err.println("查询用户");
}
2、在UserController中,添加两个端点,在这两个端点中分别调用UserService中的queryUsers方法
@GetMapping(value = "/save")
public String save() {userService.queryUsers();System.out.println("保存用户");return "订单保存成功" ;
}@GetMapping(value = "/query")
public String query() {userService.queryUsers();System.out.println("查询用户");return "查询用户成功" ;
}
4、通过@SentinelResource标记UserService中的queryUsers方法为一个sentinel监控的资源(默认情况下,sentinel只监控controller方法)
@SentinelResource("users")
public void queryUsers(){System.err.println("查询用户");
}
5、更改application.yml文件中的sentinel配置
链路模式中,是对不同来源的两个链路做监控。但是sentinel默认会给进入spring mvc的所有请求设置同一个root资源,会导致链路模式失效。因此需要关闭这种资源整合。
spring:cloud:sentinel:web-context-unify: false # 关闭context整合
6、重启服务,访问/api/user/save和/api/user/query,可以查看到sentinel的簇点链路规则中,出现了新的资源
7、添加流控规则
点击users资源后面的流控按钮,在弹出的表单中填写下面信息:
只统计从/api/user/query进入/users的资源,QPS阈值为2,超出则被限流。
8、运行测试,察看结果树:
QPS为4,超过了我们设定的阈值2。
访问/api/user/save,没有进行限流
访问/api/user/query,进行限流了
流控效果
在流控的高级选项中,还有一个流控效果选项
流控效果是指请求达到流控阈值时应该采取的措施,包括三种:
1、快速失败:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常,是默认的处理方式
2、warm up:预热模式,对超出阈值的请求同样是拒绝并抛出异常,但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值
3、排队等待:让所有的请求按照先后次序进入到一个队列中进行排队,当某一个请求最大的预期等待时间超过了所设定的超时时间时同样是拒绝并抛出异常
warm up
阈值一般是一个微服务能承担的最大QPS,但是一个服务刚刚启动时,一切资源尚未初始化(冷启动),如果直接将QPS跑到最大值,可能导致服务瞬间宕机。
warm up也叫预热模式,是应对服务冷启动的一种方案。阈值会动态变化,从一个较小值逐渐增加到最大阈值。
工作特点:请求阈值初始值是 maxThreshold / coldFactor, 持续指定时长(预热时间)后,逐渐提高到maxThreshold值,而coldFactor的默认值是3。
例如,我设置QPS的maxThreshold为10,预热时间为5秒,那么初始阈值就是 10 / 3 ,也就是3,然后在5秒后逐渐增长到10。
案例需求:给/api/user/findUserByUserId/{userId}这个资源设置限流,最大QPS为10,利用warm up效果,预热时长为5秒
配置流控规则
排队等待
排队等待:让所有的请求按照先后次序进入到一个队列中进行排队,当某一个请求最大的预期等待时间超过了所设定的超时时间时同样是拒绝并抛出异常
例如:QPS = 5,意味着每200ms处理一个队列中的请求;timeout = 2000,意味着预期等待时长超过2000ms的请求会被拒绝并抛出异常。
那什么叫做预期等待时长呢?
比如现在一下子来了12 个请求,因为每200ms执行一个请求,那么:
1、第6个请求的预期等待时长 = 200 * (6 - 1) = 1000ms
2、第12个请求的预期等待时长 = 200 * (12-1) = 2200ms
现在,第1秒同时接收到10个请求,但第2秒只有1个请求,此时QPS的曲线这样的:
如果使用队列模式做流控,所有进入的请求都要排队,以固定的200ms的间隔执行,QPS会变的很平滑
平滑的QPS曲线,对于服务器来说是更友好的。
案例需求:给/api/user/findUserByUserId/{userId}这个资源设置限流,最大QPS为10,利用排队的流控效果,超时时长设置为5s
添加流控规则
熔断规则
熔断分为两种情况
1、当目标接口的响应时间较差(慢调用的比例、数量超过阈值),2、基于异常比例、异常数量
响应时间比例
当对指定的资源接口进行访问时,1秒(统计时长)内至少5个请求(最小请求数)。
其中80%(比例阈值)的请求对应响应时间超过800ms(最大RT,response time),此时就开始熔断,熔断的时间1Os(熔断时长)。
在被熔断的期间,是不允许访问目标接口,此时可以指定降级方案(兜底方法,返回固定的结果)。
当熔断时长到达后(时间到了),放行一个请求去访问目标接口,如果此时RT(响应)时间依然超过800s(最大RT),此时就继续熔断(10s);如果放行的一个请求,响应时间没有超过800s(最大RT),则熔断结束;接下来开始进行下一轮的统计。
注意:熔断规则和流量控制的链路模式不能一起使用
异常比例
1s之内,至少5个请求,其中80%的请求抛出异常,此时熔断就开启。熔断时长10s到达之后,此时熔断器变成”半开启"状态。放行一个请求,如果这个请求依然抛出异常,此时就继续熔断。否则熔断关闭。
热点参数限流
热点参数限流是分别统计参数值不相同的请求,判断是否超过QPS阈值,同一个资源接口,针对不同的参数指,执行不同的QPS阈值。
例:userid=1为普通VIP用户,此时对该资源接口的调用每秒最多只允许调用5次。userid=3为超级VIP用户,每秒对该资源接口的访问可以访问8次。其他的普通用户,每秒只允许访问2次。
注意事项:热点参数限流对默认的spring mvc资源无效,需要利用@SentinelResource注解标记资源
实现步骤:
1、标记资源
给UserController中的/api/user/findUserByUserId/{userId}资源添加注解:
@SentinelResource("hot") // 声明资源名称
@GetMapping(value = "/findUserByUserId/{userId}")
public User findUserByUserId(@PathVariable(value = "userId") Long userId ,@RequestHeader(name = "Truth" , required = false)String header) {log.info("UserController...findUserByUserId方法执行了... ,header: {} , dateformat: {} " , header , patternProperties.getDateformat());return userService.findUserByUserId(userId) ;
}
2、热点参数限流规则
访问该接口,可以看到我们标记的hot资源出现了
@SentinelResource注解
@SentinelResource
是一个用于在 Java 应用程序中标记限流、熔断和降级资源的注解,由 Alibaba 开发的 Sentinel 库提供。这个注解广泛应用于微服务架构中,用于增强应用的容错能力和流量控制能力。
主要功能和使用方式
-
资源定义: 使用
@SentinelResource
注解的方法会被定义为一个 Sentinel 资源,Sentinel 将会监控该资源的流量。 -
参数:
value
: 定义资源的名称,通常与方法名称相同,但也可以自定义。blockHandler
: 指定一个方法,该方法用于处理被限流时的请求。这个方法应具有相同的参数类型,如限流、降级触发等情况发生时,可以调用该方法进行处理。fallback
: 指定一个方法,该方法将在原方法出现异常时调用。这个方法通常用于处理业务逻辑中的异常。
-
使用示例:
import com.alibaba.csp.sentinel.annotation.SentinelResource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MyController { @GetMapping("/someApi") @SentinelResource(value = "someApi", blockHandler = "handleBlock", fallback = "handleFallback") public String someApi() { // 你的业务逻辑 return "Hello, Sentinel!"; } // 业务接口被限流、或降级时触发改方法public String handleBlock() { return "Request has been blocked!"; } // 业务接口出现异常时触发改方法public String handleFallback() { return "An error occurred, please try again!"; } }
blockHandlerClass
:用于指定处理被限流或被阻塞请求的处理类。通过这个属性,可以将处理逻辑分离到一个特定的类中,从而使代码更加模块化和可维护。
import com.alibaba.csp.sentinel.annotation.SentinelResource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MyController { @GetMapping("/test") @SentinelResource(value = "testResource", blockHandlerClass = BlockHandlerClass.class, blockHandler = "handleBlock") public String test() { return "Hello, Sentinel!"; } } class BlockHandlerClass { // 处理被限流的请求 public static String handleBlock() { return "Request has been rate-limited!"; } }
使用场景
- 流量控制: 在高流量情况下,通过设置阈值控制请求数量,避免服务过载。
- 熔断和降级: 当某个服务出现异常或超时,可以触发降级措施以保护系统的整体稳定性。
- 监控与告警: 集成后可以实时监控请求情况,并可以配置相关告警策略
规则持久化
流控规则,热点呗则,熔断规则都可以进行持久化。
流量规则持久化
(1)在nacos配置中心,上传配置文件
2、在需要使用持久化规则的微服务上添加依赖
<!--sentinel对应的规则持久化,是基于nacos进行存储的-->
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
3、在该微服务上配置相关参数: