在本文开始之前,先思考以下两个问题:
在微服务架构中,为了保证系统的稳定性,如何防止服务被突如其来的巨大流量冲垮?
提示:以下是本篇文章正文内容,下面案例可供参考
Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。
Sentinel 具有以下特性:
丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
它与Hystrix功能相似,Hystrix常用的线程池隔离会造成线程上下切换的overhead比较大;Hystrix使用的信号量隔离对某个资源调用的并发数进行控制,效果不错,但是无法对慢调用进行自动降级;Sentinel通过并发线程数的流量控制提供信号量隔离的功能。
1、资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
2、运行指标,例如 QPS、线程池、系统负载等;
3、控制的效果,例如直接限流、冷启动、排队等。
Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
什么是熔断降级
除了流量控制以外,及时对调用链路中的不稳定因素进行熔断也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,可能会导致请求发生堆积,进而导致级联错误。
Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。
在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。
Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配,并且对于一些使用了 ThreadLocal 的场景来说会有问题(如 Spring 事务)。
1、通过并发线程数进行限制
和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
2、针对慢调用和异常对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以根据响应时间和异常等不稳定因素来快速对不稳定的调用进行熔断。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新渐进式地恢复。
3、系统自适应保护
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
4、Sentinel 是如何工作的
Sentinel 的主要工作机制如下:
1、对主流框架提供适配或者显示的 API,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。
2、根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便您定义及改变规则。
Sentinel 提供实时的监控系统,方便您快速了解目前系统的状态。
(以上信息大部分来源于Sentinel 官网https://www.oschina.net/p/sentinel?hmsr=aladdin1e1)
新建一个SpringBoot模块,在pom.xml引入以下代码(依赖)。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2.1、要使用Sentinel 进行限流,需要将被保护的对象定义为资源,资源可以是一个类、一个接口、一个方法甚至是任意一段代码。为了方便地定义资源,减少对代码的侵入性,sentinel提供了注解@SentinelResource可以直接使用:
代码如下(示例):
/**
* blockHandler中的方法在限流/降级/系统保护生效时执行
* fallback中的方法在资源抛出异常时执行
*/
@SentinelResource(value = "actorRS", blockHandler = "actorExceptionHandler", fallback = "actorFallback")
@ApiOperation("获取所有演员列表")
@RequestMapping(value="/actors",method = RequestMethod.GET)
public ActorGrid getactorlist(@RequestParam(value="current") int current,@RequestParam(value="rowCount") int rowCount
){
int total=actorservice.getactornum();
List<Actor> list=actorservice.getpageActors(current,rowCount);
ActorGrid grid=new ActorGrid();
grid.setCurrent(current);
grid.setRowCount(rowCount);
grid.setRows(list);
grid.setTotal(total);
if (rowCount != 10) {
throw new NullPointerException();
}
return grid;
}
2.2、上面的代码直接给接口添加了注解@SentinelResource,表示把该接口定义为一个资源,资源的名称用value参数指定。当资源的访问量触发了限流的条件时,blockHandler所定义的方法actorExceptionHandler会被自动调用,该方法的代码如下:
public ActorGrid actorExceptionHandler(int current, int rowCount, BlockException ex) {
ex.printStackTrace();
System.out.println("限流成功");
ActorGrid grid=new ActorGrid();
grid.setCurrent(current);
grid.setRowCount(rowCount);
grid.setRows(null);
grid.setTotal(0);
return grid;
}
2.3、该方法要求与资源定义的方法getactorlist完全一致,除了多出一个BlockException ex参数,当限流触发时,异常BlockException会被自动抛出。在这个方法中,我们在控制台打印了异常信息和限流成功的提示,并给方法返回了一个假数据。
注解@SentinelResource还有个重要的参数fallback,它表示当资源内抛出异常时会自动调用方法,这里方法actorFallback的源码如下:
public ActorGrid actorFallback(int current, int rowCount, Throwable throwable) {
System.out.println("抛出异常,触发fallback");
ActorGrid grid=new ActorGrid();
grid.setCurrent(current);
grid.setRowCount(rowCount);
grid.setRows(null);
grid.setTotal(0);
return grid;
}
可以看出这个方法的返回值和参数跟资源定义的方法getactorlist也是一样的,除了多了一个参数Throwable throwable,在这个方法体中,我们输出了抛出异常的提示并返回一个假数据。
资源已经定义好了,下面需要给资源配置限流规则,以告诉Sentinel 该资源何时才会触发过载保护。为了方便开发者的使用,Sentinel 提供了一个控制台让我们实时监控各个服务资源的流量情况,还可以在控制台中为资源定义不同的限流规则。去官网下载或者去github上下载控制台的jar包,它是一个spring boot程序:https://github.com/alibaba/Sentinel/releases。
java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar D:\sentinel-dashboard-1.8.0.jar
这里使用了8718端口开启控制台,访问http://localhost:8718/即可进入登录页面:
为了让微服务的资源流量能够被Sentinel 控制台监控,需要在微服务的配置中添加以下内容:
spring:
cloud:
sentinel:
transport:
port: 8719
dashboard: localhost:8718
eager: true
将微服务启动后,登录Sentinel 控制台,账号密码都是sentinel,可以看到以下效果:
可以看到我们的微服务sakila-service已经成功地接入到sentinel控制台了,菜单实时监控和簇点链路用于查看各个资源的流量信息,菜单流控规则、降级规则、热点规则、系统规则、授权规则、集群流控对应sentinel支持的六大类不同的限流方式,每种方式可以从不同的角度完成对系统的过载保护,这里以最常规的“流控规则”为例进行说明。
进入“流控规则”的菜单,点击右上角的按钮新增流控规则,可以看到下面的效果:
在这里我们配置了资源名为actorRS,表示对该资源做流量控制,阈值类型选择QPS表示资源每秒请求的数量,单机阈值设置为5表示该资源每秒调用的频率超过5次就会触发流量控制的保护,去执行blockHandler中指定的方法。
新增好该流控规则后,来测试一下限流的效果,打开浏览器调用该资源对应的接口:
http://localhost:8082/actors?current=1&rowCount=1
由于此时该接口抛出空指针异常,触发fallback回退机制,控制台打印出了相应的提示:
下面调用接口:
http://localhost:8082/actors?current=1&rowCount=10,为了看到限流效果,频繁调用使QPS达到5以上,可以看到,触发限流后控制台有了相应的效果:
看到以上效果便说明sentinel的流量控制规则起了作用,当被保护资源达到阈值上限后资源将拒绝提供服务,返回blockHandler方法中指定的假数据。以上便是sentinel在微服务中的使用方法,关于更多的使用方法,读者可以参考官方文档(部分代码有参考网上,仅用于学习,如有冒犯敬请谅解!)。