1 简介
Sentinel是阿里开源的项目,是一款面向分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来保障服务的稳定性。
核心思想是:根据对应资源配置的规则来为资源执行相应的流控/降级/系统保护策略,官网地址为:https://github.com/alibaba/Sentinel/wiki。
Sentinel 具有以下特征:
丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 的主要特性如下图:
Sentinel实现限流、隔离、降级、熔断等功能,本质要做的就是两件事情:
统计数据:统计某个资源的访问数据(QPS、RT等信息)
规则判断:判断限流规则、隔离规则、降级规则、熔断规则是否满足
这里的资源就是希望被Sentinel保护的业务,例如项目中定义的controller方法就是默认被Sentinel保护的资源。
Sentinel VS 网关(Gateway)
sentinel 和 网关 都可以限流,区别是:
侧重点不同:
Gateway作为流量入口侧重于对“后端资源”进行流控;
Sentinel作为一个嵌入到应用的SDK侧重于对“自身资源“的流控。定位不同:
Gateway本身不是专门做流控的,流控只是其中一个能力,网关用于控制入口流量,无法对进入网关后的流量(即服务与服务之间的调用流量)兜底;
Sentinel 关注微服务全链路、全方位的流控降级与容错,可以在任意点位进行容错保护。使用场景不同:
网关流控更多的时候,是针对 后端服务做一个 整体的限流,比如对 商品微服务限流qps 10000,同时支持优先级限流,比如 整体限流10000,同时 A接口限流 5000 ,B接口限流 6000,C接口限流 3000,这里是可以通过优先级设置,使得就算是 整体流量超过10000后,如果A接口限流还没达到,那么A接口就不会被限制(因为A接口限流规则 优先级 大于 整体流量限流规则);
sentinel 针对的是具体的接口,甚至是针对接口中的入参,比如,可以针对接口中入参为不同供应商的参数,进行限流,在极端情况下,可以通过限制一些不重要的供应商(成交量低)来保护核心供应商(成交量高)。
2 架构
Sentinel 分为两个部分:
核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
所以,所有的统计以及计算,都是在客户端完成的。
上图是来自官网的总体架构图,这张图上可以清晰的看到整个流量控制以责任链的模式进行的,每一个slot负责特定的处理。
NodeSelectorSlot
负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;ClusterBuilderSlot
则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;StatisticSlot
则用于记录、统计不同纬度的 runtime 指标监控信息;FlowSlot
则用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制;AuthoritySlot
则根据配置的黑白名单和调用来源信息,来做权限控制;DegradeSlot
则通过统计信息以及预设的规则,来做熔断降级;SystemSlot
则通过系统的状态,例如 load1 等,来控制总的入口流量,来做系统规则控制;
3 限流/熔断执行过程及源码
微服务限流
微服务限流是指在规定的时间段内限制服务的请求量,以保护系统。主要作用是防止突发流量而导致系统崩溃,比如秒杀、抢购等场景,就需要对系统中微服务进行限流。
现实中限流例子:银行取号系统。
微服务熔断
服务熔断是指调用方访问服务时通过断路器做代理进行访问,断路器会持续观察服务返回的成功、失败的状态,当失败超过设置的阈值时断路器打开,请求就不能真正地访问到服务了。当监控到该微服务正常工作后,再次恢复该调用链路。
微服务降级
在服务发生熔断后,一般会让请求走事先配置的处理方法,这个处理方法就是一个降级逻辑。服务降级是对非核心、非关键的服务进行降级。
微服务的限流、降级、熔断等容错机制不可避免回造成用户请求失败或变慢,从而在一定程度上影响用户体验,所以策略的制定需要以系统压测的结果为参考,并在用户体验与系统稳定性之间进行平衡取舍。
Sentinel 限流执行过程如下:
Sentinel限流、熔断降级源码架构图
6 规则管理以及推送
Sentinel 支持以下几种规则:流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则 和 热点参数规则。
一般来说,规则的推送有下面三种模式:
推送模式 | 说明 | 优点 | 缺点 |
---|---|---|---|
原始模式 | API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource ) |
简单,无任何依赖 | 不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境 |
Pull 模式 | 扩展写数据源(WritableDataSource ), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等 |
简单,无任何依赖;规则持久化 | 不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。 |
Push 模式 | 扩展读数据源(ReadableDataSource ),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。
|
规则持久化;一致性;快速 | 引入第三方依赖 |
生产环境下,一般采用Push模式,即:控制台将配置规则推送到远程配置中心,例如Zookeeper。Sentinel客户端监听Zookeeper,获取配置变更的推送消息,完成本地配置更新。 架构图如下
7 实时监控
Sentinel 客户端会记录资源访问的秒级数据(若没有访问则不进行记录)并保存在本地日志中,Sentinel 控制台可以通过Sentinel 客户端预留的 HTTP API 从秒级监控日志中拉取监控数据,并进行聚合,也就是说同一个服务下的所有机器的簇点信息会被汇总,并且秒级地展示在"实时监控"下。
目前 Sentinel 控制台中监控数据聚合后直接存在内存中,未进行持久化,且仅保留最近 5 分钟的监控数据。
当然,如果监控数据要持久化,也可以扩展实现,官网有demo。
8 使用
管理端配置和使用demo见官网。
8.1 按url限流
设置 sentinel 提供的 CommonFilter 来拦截所有的访问
@Configuration
public class webConfig {
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns("/*");
registration.setName("sentinelFilter");
registration.setOrder(1);
return registration;
}
}
或者设置
spring.cloud.sentinel.filter.enabled = true
8.2 按资源限流(@SentinelResource)
资源:可以是任何东西,一个服务,服务里的方法,甚至是一段代码。
@SentinelResource 注解包含以下属性:
value:资源名称,必需项(不能为空)
entryType:entry 类型,可选项(默认为 EntryType.OUT)
blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
fallback:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 - exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
返回值类型必须与原函数返回值类型一致;
方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常;
fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
demo如下:
@GetMapping("/fallback/{id}")
//同时添加SentinelResource注解的fallback和blockHandler属性 exceptionsToIgnore被标注的异常将会被 原样抛出
@SentinelResource(value = "falllback", fallback = "fallbackHandler", blockHandler = "blockHandler", exceptionsToIgnore = {ArrayIndexOutOfBoundsException.class})
public String fallback(@PathVariable Long id){
if (id < 0 || id > 3) {
throw new NullPointerException("无对应的记录");
}
return "success";
}
//保证方法签名与原方法一致或加一个 Throwable 类型的参数
public String fallbackHandler(Long id, Throwable e) {
return "出现未知的记录";
}
//处理Sentinel限流
public String blockHandler(Long id, BlockException e){
return "BlockException限流";
}
fallback:若本接口出现未知异常,则调用fallback指定的接口。
blockHandler:若本次访问被限流或服务降级,则调用blockHandler指定的接口。
8.3 控制台配置
-
按url限流: 如下图,控制台配置资源名为访问路径,一般为controller下全路径
-
按资源限流: 如下图,控制台配置资源名为@SentinelResource注解对应的value值。