前言
接着上章【SpringCloudAlibaba - Nacos服务发现和配置管理】,继续学习Sentienl 限流 熔断
一、Sentinel是什么?
Sentinel诞生于阿里巴巴,其主要目标是流量控制和服务熔断,2018年,Sentinel演变为一个开源项目现如今成为了Spring Cloud Alibaba的一个子项目。Sentinel是通过限制并发线程的数量来减少不稳定资源的影响,而不是使用线程池,省去了线程切换的性能开销。
当资源的响应时间变长时,线程将开始被占用。当线程数累积到一定数量时,新的传入请求将被拒绝。反之亦然,当资源恢复并变得稳定时,占用的线程也将被释放,新请求将被接受。
除了限制并发性外,Sentinel可以根据响应时间降级不稳定资源也是保证可靠性的有效方法。当资源的响应时间太大时,将在指定的时间窗口中拒绝所有对该资源的访问。-- 熔断机制
此外,Sentinel支持的熔断降级维度更多,可对多种指标进行流控、熔断,且提供了实时监控和控制面板,功能更为强大。
二、Sentinel限流实战
1.安装Sentinel Server服务端
Sentinel 提供了现成的服务端供我们使用,去官网下载,下载之后通过命令行启动
java -jar -Dserver.port=1111 sentinel-dashboard-1.6.0.jar
访问:http://127.0.0.1:1111 进入控制台,使用 账号:sentinel,密码:sentinel登录。
注意:只有1.6.0及以上版本才有登录页面。默认用户名和密码都是sentinel。对于用户登录的相关配置可以在启动命令中增加下面的参数来进行配置:
- -Dsentinel.dashboard.auth.username=sentinel: 用于指定控制台的登录用户名为 sentinel;
- -Dsentinel.dashboard.auth.password=123456: 用于指定控制台的登录密码为 123456;如果省略这两个参数,默认用户和密码均为 sentinel
- -Dserver.servlet.session.timeout=7200: 用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟;
- -Dserver.port=1111:配置端口
2. Sentinel 客户端接入
2.1 导入依赖
修改用户服务 springcloudalibaba-user-server,加入sentinel依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2.2 在application.yml中配置Sentinel
spring:
cloud:
sentinel:
transport:
dashboard: localhost:1111
2.3 资源限流
Sentinel为我们提供了 @SentinelResource 注解标记需要限流的资源。 修改UserController,代码如下:
@GetMapping("/user/{id}")
//限流降级
@SentinelResource(value="user",blockHandler="exceptionHandler")
public User getById(@PathVariable Long id){
System.out.println(notify);
return new User(id,"zs:"+id, "我是zs");
}
// 限流与阻塞处理 : 参数要和 被降级的方法参数一样
public User exceptionHandler(@PathVariable Long id, BlockException ex) {
ex.printStackTrace();
System.out.println("限流了...");
return new User(-1L,"限流了","限流了");
}
提示:这里通过@SentinelResource的value属性为资源取名为 “user” ,后续我们可以根据该资源名来进行限流。
同时这里通过 blockHandler 属性我配置了一个限流降级方法,即当“user”资源触发限流了会调用blockHandler指向的降级方法返回拖地数据,不至于抛出默认的限流异常信息给客户端(一串英文用户也看不懂) ,需要注意的是:降级方法要和被限流的方法参数一致,然后加上 BlockException异常对象。
也可以通过 blockHandlerClass 属性把降级方法写在一个专门的类中,如:
@SentinelResource(value="user",blockHandler="exceptionHandler"
,blockHandlerClass=ExceptionUtil.Class)
降级类
public final class ExceptionUtil {
public static User exceptionHandler(Long id ,lockException ex) {
//...
}
}
3.Sentinel设置限流策略
启动应用 springcloudalibaba-user-server ,然后通过浏览器访问 http://localhost:1010/user/11 ,然后登录Sentinel控制台,在“实时监控”列表中可以看到资源的相关监控信息的
在 “族点链路” 列表中可以看到资源的调用链 ,并且可以通过“流控”按钮设置流控规则
也可以在“流量控制”菜单中针对资源进行限流规则的设置。如下:
添加了一个流控规则,资源名对应客户端 @SentinelResource(value=“user”… 注解的资源,通过QPS限流(每秒请求数量),阈值是 1 ,意思是“user”这个资源每秒只能有1个请求进来,多余的请求会触发限流,返回降级数据。
记得进行测试!
4.热点限流
一种特殊的动态限流规则,用于限制动态的热点资源 , 比如对同一个用户的请求频率做限定,比如对参数进行限定,比如对参数的值做限定(比如对商品ID为1的资源做限流)。
4.1.参数限流
参数限流就是 对资源的参数进行限流,我们来编写一个方法,接受两个参数:p1,和p2并设置好限流降级。
//限流降级
@SentinelResource(value="/parameterLimit",blockHandler="parameterLimitHandler")
@GetMapping(value="/parameterLimit")
public String parameterLimit(@RequestParam("p1") String p1 ,@RequestParam("p2") String p2){
return "parameterLimit方法调用成功...";
}
// 限流与阻塞处理
public String parameterLimitHandler(@RequestParam("p1") String p1 ,@RequestParam("p2") String p2,BlockException ex) {
return "限流了...";
}
配置热点规则 , 对第一个参数限流 , 当第一个参数超过了1的QPS就熔断降级。
4.2.参数值限流
对某一个参数的值满足某种条件的时候就进行限流,如下配置:
意思是第一个参数的值为 haha 的时候限流阈值为10 , 超过 QPS > 10的并发就限流。
举例:应用场景比如说热点商品进行限流。
5.系统规则
5.1 配置全局限流规则
系统规则可以看做是总的限流策略,所有的只要都要受到系统规则的限制。
设置:最大并发只能允许 10 个线程数,并且是作用于全局的。
6.Gateway使用Sentinel限流
Spring Cloud Gateway 作为微服务的网关,它是微服务的访问入口,当请求的流量洪峰到来我们可以在Gateway网关层通过Sentinel对请求进行流控,把好第一道关。
6.1 导入依赖
<!-- 限流和gataway使用-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
6.2 配置Sentinel地址
修改yml配置,增加Sentinel服务地址
spring:
cloud:
sentinel:
transport:
dashboard: localhost:1111
6.3 配置限流规则
启动Gateway,登录sentinel控制台,对url资源进行流控限制,配置方式和前面的配置方式一样
6.4 限流降级
@Configuration
public class SentinelConfig {
public SentinelConfig(){
GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
return ServerResponse.ok().body(Mono.just("限流啦,请求太频繁"),String.class);
}
});
}
}
7.Nacos持久化限流规则
7.1 整合Nacos持久化限流规则
第一步:导入基础依赖,springcloudalibaba-user-server为例子,修改pom增加Sentinel和Nacos持久化配置依赖 sentinel-datasource-nacos ,如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--Sentinel和Nacos做持久的-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.5.2</version>
</dependency>
</dependencies>
第二步:配置持久化nacos地址
spring:
cloud:
sentinel:
datasource:
flow:
nacos: #限流持久配置
server-addr: localhost:8848 #使用nacos的持久
dataId: application-user-dev #获取限流的数据源的dataId
groupId: DEFAULT_GROUP
rule-type: flow #类型:限流
这里其实是在之前的配置基础上增加了 spring.cloud.sentinel.datasource持久化数据源的配置,对应了NacosDataSourceProperties 配置类具体含义如下:
- spring.cloud.sentinel.datasource.ds.nacos.server-addr:nacos的访问地址
- spring.cloud.sentinel.datasource.ds.nacos.groupId:nacos中存储规则的groupId
- spring.cloud.sentinel.datasource.ds.nacos.dataId:nacos中存储规则的dataId
- spring.cloud.sentinel.datasource.ds.nacos.rule-type:该参数是用来定义存储的规则类型。所有的规则类型可查看枚举类:…datasource.RuleType,每种规则的定义格式可以通过各枚举值中定义的规则对象来查看,比如限流规则可查看:…flow.FlowRule
第三步:编写测试用的Controller这个controller我们待会用来做限流测试
@RestController
public class TempController {
@GetMapping("/hello")
public String hello() {
return "测试数据";
}
}
7.2 Nacos持久化Sentinel限流规则
第一步:在配置列表增加配置如下
[
{
"resource": "/hello",
"limitApp": "default",
"grade": 1,
"count": 10,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
-
resource:对那个资源进行限流
-
limitApp:这个是流控的调用者,default 代表不区分调用者
-
grade:限流方式0是根据并发数量限流,1是表根据QPS来限流
-
count:限流阈值,达到这个阈值就被限流,触发降级。
-
strategy:基于调用链的流控制策略。0 直接,1 关联 2 链路
-
controlBehavior:流控效果,0 直接拒绝,1是Warm Up,2是匀速排队
-
clusterMode:是否为集群
上面的配置项目对应了 com.alibaba.csp.sentinel.slots.block.flow.FlowRule 限流规则类。程序启动,Sentinel通过 NacosDataSource 从Nacos中查找配置。具体效果如:
注意:这里的DataId 和 Group其实是和上一步 yml配置中的DataId和Group相对应,这里配置的是一个数组,意思是可以配置多个限流策略
第二步:启动测试 ,启动springcloudalibaba-user-server-1010 ,向 /hello 资源发起访问,然后观察Sentinel控制台流控规则是否有了一条限流策略。
7.3 Nacos持久化Sentinel降级规则
第一步:在Nacos配置列表增加文件 如:application-user-degrade-dev ,内容如下
[
{
"resource": "GetUserByID",
"grade": 0,
"count": 10,
"timeWindow": 5
}
]
-
resources : 资源名
-
grade : 慢调用比例 0 ;异常比例 1 ;异常数 2;
-
count : 最大RT,最大平均响应时间
-
timeWindow :时间窗口,即熔断时长
第二步:项目中增加配置
spring:
cloud:
sentinel:
datasource:
degrade: #降级的配置
nacos:
server-addr: 127.0.0.1:8848
dataId: application-user-degrade-dev
groupId: DEFAULT_GROUP
rule-type: degrade
第三步:启动测试,观察sentinel的熔断规则列表
三、Sentinel熔断
1. Sentinel熔断是什么?
Sentinel的服务熔断机制会对调用链上的某个不稳定(宕机,异常,超时)的资源,做出请求限制,快速失败,避免影响到其它的服务而导致级联错误。资源熔断后,在后续的一定时间(时间窗口)之内,对该服务的请求都自动熔断,抛出 DegradeException异常。
Sentinel拥有比Hystrix更强大和丰富的功能,能满足我们的各种应用场景,并且经历过淘宝双十一的考验,是微服务架构中熔断机制的不二选择。
2. Sentnel熔断实战
2.1.资源熔断降级
修改springcloudalibaba-user工程,修改UserController ,通过@SentinelResource注解的fallback 属性指定降级方法。
// 限流降级
public User exceptionHandler(@PathVariable Long id, BlockException ex) {
ex.printStackTrace();
System.out.println("限流了...");
return new User(-1L,"限流了","限流了");
}
// 熔断降级,参数和返回值与源方法一致
public User getByIdfallback(@PathVariable Long id){
System.out.println(notify);
return new User(id,"zs:"+id, "熔断托底了");
}
@GetMapping("/user/{id}")
//限流降级
@SentinelResource(value="user",blockHandler="exceptionHandler",fallback = "getByIdfallback")
public User getById(@PathVariable Long id){
int i = 1 / 0; //方法异常,触发熔断
return new User(id,"zs:"+id, "我是zs");
}
方法中通过 int i = 1 / 0; 模拟异常,然后会熔断触发降级调用降级方法 。 通过 fallback 属性指定熔断的降级方法 ,熔断方法参数也要和被熔断方法参数一致。
注意:这里可以通过 @SentinelResource注解的 exceptionsToTrace 属性忽略异常,即针对某个异常不熔断。
2.2.配置降级策略
在Sentinel控制台,在族点链路菜单中找到“user”资源,然后点击“降级”按钮添加降级策略,如下:
这里的降级策略为“RT”,大概意思是:如果并发数大于5 (QPS > 5) ,然后平均响应时间大于200毫秒,那么接下来的2秒钟之内对该资源的请求会被熔断降级。
3. Feign整合Sentinel熔断
3.1.开启Sentinel
OpenFeign与Sentinel组件集成除了引入sentinel-starter依赖关系之外,还需要在属性文件中启用Sentinel支持:feign.sentinel.enabled=true
feign:
sentinel:
enabled: true #熔断
3.2 给Feign接口降级
这里跟Feign开启Hystrix降级一样,还是可以使用fallback属性
@FeignClient(value = "user-server",fallback = UserClientFallback.class)
public interface UserClient {
@GetMapping("/user/{id}")
User getById(@PathVariable Long id);
}
3.3 编写降级类
@Component
public class UserClientFallback implements UserClient {
@Override
public User getById(Long id) {
return new User(-1L,"无此用户","无此用户");
}
}
到这里我们已经完成了Feign和Sentinel的兼容使用,是不是很简单呢,因为它的集成方式和Hystrix简直一模一样
总结
对 Sentienl 限流 熔断 基础知识学习和了解。