Spring Cloud Alibaba实践 --Sentinel
sentinel介绍
Sentinel的官方标题是:分布式系统的流量防卫兵。从名字上来看,很容易就能猜到它是用来作服务稳定性保障的。对于服务稳定性保障组件,如果熟悉Spring Cloud的用户,第一反应应该就是Hystrix。但是比较可惜的是Netflix已经宣布对Hystrix停止更新。那么,在未来我们还有什么更好的选择呢?除了Spring Cloud官方推荐的resilience4j之外,目前Spring Cloud Alibaba下整合的Sentinel也是用户可以重点考察和选型的目标。
如果某一个服务不可用或者宕机了,就会出现线程池里所有线程都因等待响应而被阻塞, 从而造成服务雪崩。解决雪崩问题的常见方式,也是微服务常见的治理策略:
- 服务超时:设定超时时间,请求超过一定的时间没有响应就返回错误信息,不会无休止等待。
- 资源隔离:将系统按照一定的规则划分为若干个服务模块,各个模块之间相对独立,无强依赖。常见的隔离方式有:线程池隔离和信号量隔离。
- 流量控制:限制业务访问的QPS,避免服务因流量的突增而故障。
- 服务熔断:有断路器统计业务执行的异常比例,如果超出阈值则会熔断业务,拦截访问该业务的一切请求,直到该业务错误率降到阈值以下。
- 服务降级:当某个服务熔断之后,服务将不再被调用,此时客户端可以自己准备一个本地的fallback(回退)回调,返回一个缺省值。例如,在商品详情页一般都会展示商品的介绍信息,一旦商品详情页系统出现故障无法调用时,会直接获取缓存中的商品介绍信息返回给前端页面。
Sentinel 基本概念:
资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
流量控制
Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状。
流量控制有以下几个角度:
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
- 运行指标,例如 QPS、线程池、系统负载等;
- 控制的效果,例如直接限流、冷启动、排队等。
熔断降级
-
通过并发线程数进行限制
Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。 -
通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
Sentinel 的主要工作机制如下:
- 对主流框架提供适配或者显示的 API,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。
- 根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便您定义及改变规则。
- Sentinel 提供实时的监控系统,方便您快速了解目前系统的状态。
Sentiner与hystrix的区别:
安装并整合sentinel客户端
Sentinel的使用分为两部分:
- sentinel-dashboard:与hystrix-dashboard类似,但是它更为强大一些。除了与hystrix-dashboard一样提供实时监控之外,还提供了流控规则、熔断规则的在线维护等功能。
- 客户端整合:每个微服务客户端都需要整合sentinel的客户端封装与配置,才能将监控信息上报给dashboard展示以及实时的更改限流或熔断规则等。
(1)启动sentinel-dashboard
下载地址:sentinel-dashboard-1.6.0.jar
其他版本:Sentinel/releases
通过命令启动:
java -jar sentinel-dashboard-1.6.0.jar
默认情况下,sentinel-dashboard以8080端口启动。由于sentinel-dashboard是一个标准的spring boot应用,所以如果要自定义端口号等内容的话,可以通过在启动命令中增加参数来调整,比如:
java -jar sentinel-dashboard-1.6.0.jar --server.port=8888
所以可以通过访问:localhost:8888 来验证是否已经启动成功,如果一切顺利的话,可以看到登录页面(默认用户名和密码都是sentinel)。
(2)整合Sentinel
依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
配置:
spring.application.name=alibaba-sentinel-rate-limiting
server.port=8002
# sentinel dashboard
spring.cloud.sentinel.transport.dashboard=localhost:8888
主类:
package com.example.demospringboot;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@Slf4j
public class DemospringbootApplication {
public static void main(String[] args) {
SpringApplication.run(DemospringbootApplication.class, args);
}
@Slf4j
@RestController
static class TestController {
@GetMapping("/hello")
public String hello() {
return "Hello World";
}
}
}
启动应用,并执行curl命令:
此时,在上面启动的Sentinel Dashboard中就可以当前我们启动的alibaba-sentinel-rate-limiting这个服务以及接口调用的实时监控了。具体如下图所示:
流控限流
配置接口限流规则
在完成了上面的两节之后,我们在alibaba-sentinel-rate-limiting服务下,点击簇点链路菜单,可以看到如下界面:
通过点击流控按钮,来为该接口设置限流规则,比如:
流控模式:
- 直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认模式。
- 关联:统计与当前相关的另一个资源,触发阈值时,对当前资源限流。
- 链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流,即只限流从指定接口进来的
流控效果:
-
快速失效:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。
-
warm up:预热模式,对超出阈值的请求同样是拒绝,并抛出异常。但这种模式阈值会动态变化,从一个较小的值逐渐增加到最大值。请求阈值初始值是 threshold/coldFactor(默认值是3),持续指定时长后,逐渐提高到threshould值。例如,我设置QPS的threshold的为10,预热时间为5秒,那么初始阈值就是10/3,也就是3,然后在5秒后逐渐增长到10.
-
排队等待:让所有请求按照先后次序进入一个队列中排队执行,两个请求的间隔不能小于指定时长。例如:QPS = 5,意味着每200ms处理一个队列中的请求;timeout =2000,意味着预期等待超过2000ms的请求会被拒绝并抛出异常。
点击新增按钮之后,可以看到如下界面:
其实就是左侧菜单中流控规则的界面,这里可以看到当前设置的所有限流策略。在完成了上面所有内容之后,我们可以尝试一下快速的调用这个接口,看看是否会触发限流控制,比如:
可以看到,快速的调用/hello接口之后,调用被限流了。
在 sentinel-dashboard界面,也可以看到对应统计:
Sentinel使用Nacos存储规则
Dashboard中设置的限流规则在应用重启之后就丢失了。Sentinel自身就支持了多种不同的数据源来持久化规则配置,目前包括以下几种方式:
- 文件配置
- Nacos配置
- ZooKeeper配置
- Apollo配置
本文我们使用Spring Cloud Alibaba的中整合的配置中心Nacos存储限流规则。前置条件,启动Nacos,详见 Spring Cloud Alibaba实践 --Nacos
默认配置下启动后,它们的访问地址为:
- Nacos:http://localhost:8848/nacos
- Sentinel Dashboard:http://localhost:8080/
(1)在Spring Cloud应用的pom.xml中引入Spring Cloud Alibaba的Sentinel模块和Nacos存储扩展:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.5.2</version>
</dependency>
(2)在Spring Cloud应用中添加配置信息
spring.application.name=alibaba-sentinel-rate-limiting
server.port=8002
# sentinel dashboard
spring.cloud.sentinel.transport.dashboard=localhost:8888
# ds-sentinel-nacos-datasource
spring.cloud.sentinel.datasource.ds.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds.nacos.dataId=${spring.application.name}-sentinel
spring.cloud.sentinel.datasource.ds.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds.nacos.rule-type=flow
(3)Nacos中创建限流规则的配置
注:Data ID、Group就是上面第二步中配置的内容。配置格式选择JSON,并在配置内容中填入下面的内容:
[
{
"resource": "/hello",
"limitApp": "default",
"grade": 1,
"count": 5,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
配置规则是一个数组类型,数组中的每个对象是针对每一个保护资源的配置对象,每个对象中的属性解释如下:
- resource:资源名,即限流规则的作用对象
- limitApp:流控针对的调用来源,若为 default 则不区分调用来源
- grade:限流阈值类型(QPS 或并发线程数);0代表根据并发数量来限流,1代表根据QPS来进行流量控制
- count:限流阈值
- strategy:调用关系限流策略
- controlBehavior:流量控制效果(直接拒绝、Warm Up、匀速排队)
- clusterMode:是否为集群模式
(4)启动应用,curl localhost:8002/hello
测试
如果一些顺利,可以看到类似下面的日志,代表已经成功从Nacos加载了一条限流规则:
2023-12-09 19:00:04.887 INFO 13800 --- [ main] o.s.c.a.s.c.SentinelDataSourceHandler : [Sentinel Starter] DataSource ds-sentinel-nacos-datasource load 1 FlowRule
在Sentinel Dashboard中就可以看到当前我们启动的alibaba-sentinel-rate-limiting
服务。点击左侧菜单中的流控规则,可以看到已经存在一条记录了,具体如下:
注:在完成了上面的整合之后,对于接口流控规则的修改就存在两个地方了:Sentinel控制台、Nacos控制台。
这个时候,需要注意当前版本的Sentinel控制台不具备同步修改Nacos配置的能力,而Nacos由于可以通过在客户端中使用Listener来实现自动更新。所以,在整合了Nacos做规则存储之后,需要知道在下面两个地方修改存在不同的效果:
- Sentinel控制台中修改规则:仅存在于服务的内存中,不会修改Nacos中的配置值,重启后恢复原来的值。
- Nacos控制台中修改规则:服务的内存中规则会更新,Nacos中持久化规则也会更新,重启后依然保持。
@SentinelResource对某个方法资源调用限流
在实际应用过程中,我们可能需要限流的层面不仅限于接口,而是对于某个方法的调用限流,对于某个外部资源的调用限流等都希望做到控制。这个时候我们就不得不手工定义需要限流的资源点,并配置相关的限流策略等内容了。使用@SentinelResource
注解可以灵活的定义控制资源以及如何配置控制策略。
主启动类:
在应用主类中增加注解支持的配置Bean
package com.example.demospringboot;
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@Slf4j
public class DemospringbootApplication {
public static void main(String[] args) {
SpringApplication.run(DemospringbootApplication.class, args);
}
// 注解支持的配置Bean
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
Service:
在需要通过Sentinel来控制流量的地方使用@SentinelResource
注解,自定义资源点;
并通过@SentinelResource
注解的blockHandler
属性实现限流的异常处理
package com.example.demospringboot;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class TestService {
@SentinelResource(value = "doSomeThing", blockHandler = "exceptionHandler")
public void doSomeThing(String str) {
log.info(str);
}
// 限流与阻塞处理
public void exceptionHandler(String str, BlockException ex) {
log.error( "blockHandler-> " + str, ex);
}
}
Controller:
在Web层调用这个被保护的方法
package com.example.demospringboot;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@RestController
public class TestController {
@Autowired
private TestService testService;
@GetMapping("/hello")
public String hello() {
testService.doSomeThing("hello " + new Date());
return "Hello World";
}
}
启动测试应用,启动Sentinel-Dashboard。 curl localhost:8002/hello
发一个请求到/hello接口上,使得Sentinel-Dashboard上可以看到如下图所示的几个控制点:
可以看到,多了一个doSomeThing资源点。可以通过界面为这个资源点设置限流规则,比如将其QPS设置为2。由于/hello资源不设置限流规则,所以只要请求/hello接口,就可以直接模拟调用doSomeThing资源,来观察限流规则是否生效。只要QPS超过2,那么就会出现如下的错误返回,代表限流策略生效了。
2023-12-09 12:45:58.015 INFO 11368 --- [nio-8002-exec-7] com.example.demospringboot.TestService : hello Sat Dec 09 12:45:58 CST 2023
2023-12-09 12:45:58.378 TRACE 11368 --- [nio-8002-exec-8] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.demospringboot.TestController#hello()
2023-12-09 12:45:58.401 ERROR 11368 --- [nio-8002-exec-8] com.example.demospringboot.TestService : blockHandler-> hello Sat Dec 09 12:45:58 CST 2023
com.alibaba.csp.sentinel.slots.block.flow.FlowException: null
熔断降级
@SentinelResource
注解除了可以用来做限流控制之外,还能实现与Hystrix类似的熔断降级策略。
这里只需对TestService类改造,让doSomeThing方法持续抛出异常,触发fallback属性指定的具体方法名进行降级处理。
package com.example.demospringboot;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class TestService {
@SentinelResource(value = "doSomeThing", fallback = "fallbackHandler")
public void doSomeThing(String str) {
log.info(str);
throw new RuntimeException("发生异常");
}
public void fallbackHandler(String str) {
log.error("fallbackHandler-> " + str);
}
}
在Sentinel-Dashboard名为doSomeThing的资源点上,点击”降级“按钮,为该资源设置降级规则。这里使用异常比例策略,比例设置为0.1(即:10%的异常率),时间窗口设置为2(秒):
验证熔断降级,根据上面的降级策略配置,当doSomeThing方法的调用QPS >= 5时,如果异常率超过10%,那么后续2秒内的调用将直接触发熔断降级:
2023-12-09 13:30:21.332 TRACE 4248 --- [io-8002-exec-10] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.demospringboot.TestController#hello()
2023-12-09 13:30:21.333 ERROR 4248 --- [io-8002-exec-10] com.example.demospringboot.TestService : fallbackHandler-> hello Sat Dec 09 13:30:21 CST 2023
2023-12-09 13:30:21.678 TRACE 4248 --- [nio-8002-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.demospringboot.TestController#hello()
2023-12-09 13:30:21.678 ERROR 4248 --- [nio-8002-exec-2] com.example.demospringboot.TestService : fallbackHandler-> hello Sat Dec 09 13:30:21 CST 2023
参考:
https://www.didispace.com/spring-cloud/spring-cloud-alibaba-sentinel-2-5.html
https://zhuanlan.zhihu.com/p/565074363?utm_id=0
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!