一. Sentinel概念
1. 什么是Sentinel?
Sentinel是阿里中间件团队研发面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。于2012年诞生,后续在阿里巴巴集团内部迅速发展,成为基础技术模块,覆盖了所有的核心场景,Sentinel也因此积累了大量的流量归整场景及生产实践。最终在2018年7月宣布对外界开源。
Sentinel的基本概念:
资源: Sentinel 的关键概念,可以是Java应用程序中任何内容,通过Sentinel API定义的代码,能够被Sentinel保护起来,大部份情况下,可以使用方法签名,URL,甚至服务名称作为资源名
**规则:**围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
Sentinel分为两个部分:
控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器,也就是sentinel-dashboard-1.8.1.jar。
核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
2. Sentinel功能特性
从上图可知Sentinel的功能特性很多以及应用场景非常广泛,以下就限流、熔断降级这几个常见的功能特性以及意义进行简要说明
**限流:**限流同时也叫做流量控制,原理是监控应用流量的QPS或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
**熔断降级:**除了流量控制以外,及时对调用链路中的不稳定因素进行熔断也是Sentinel的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积,进而导致级联错误。Sentinel当检测到调用链路中某个资源出现不稳定的表现(请求响应时间长或异常比例升高),则对这个资源的调用进行限制,让请求快速失败,避免影响到其他资源而导致级联故障。
熔断: 拒绝流量访问,当系统恢复正常时关闭熔断。
**降级:**将次要服务降级,停止服务,将系统资源放出来给核心功能
3. Sentinel VS Hystrix
以下摘自Sentinel官方文档,详情点击 Sentinel 与 Hystrix 的对比
Sentinel Hystrix
隔离策略 信号量隔离 线程池隔离/信号量隔离
熔断降级策略 基于响应时间或失败比率 基于失败比率
实时指标实现 滑动窗口· 滑动窗口(基于 RxJava)
规则配置 支持多种数据源 支持多种数据源
扩展性 多个扩展点 插件的形式
基于注解的支持 支持 支持
限流 基于 QPS,支持基于调用关系的限流 有限的支持
流量整形 支持慢启动、匀速器模式 不支持
系统负载保护 支持 不支持
控制台 开箱即用,可配置规则、查看秒级监控、机器发现等 不完善
常见框架的适配 Servlet、Spring Cloud、Dubbo、gRPC 等 Servlet、Spring Cloud Netflix
Sentinel 和 Hystrix 的原则是一致的,但是在限制手段上,Sentinel和Hystrix采取了完全不一样的方法。
Hystrix: 通过线程池隔离,来对依赖(在Sentinel的概念对应资源)进行了隔离。好处在于资源之间做到最彻底的隔离,缺点是除了增加了线程切换的成本,还需要预先给各个资源做线程池大小的分配。
Sentinel : 通过并发线程数进行限制和响应时间对资源进行降级两种手段。
二. Docker部署Sentinel Dashboard
1. 拉取镜像
docker pull bladex/sentinel-dashboard
2. 启动容器
docker run --name sentinel -d -p 8858:8858 -d bladex/sentinel-dashboard
3. 访问测试
访问:http://IP:8858
用户名/密码:sentinel/sentinel
三. Sentinel网关流控
1. 网关流控定义
Sentinel支持对Spring Cloud Gateway、Zuul等主流的API Gataway 进行限流,作用在网关的流控称之为网关流控,其实现原理请点击网关限流进入官方Wiki查看。
这里只把原理图从官方文档摘出来,需多关注图中提到的模块名和几个类名,因为都是核心级别的存在。
规则类型gw-flow和gw-api-group为网关流控规则,具体类型请查看规则类型枚举RuleType
/**
* flow 流控规则
*/
FLOW("flow", FlowRule.class),
/**
* degrade 降级规则
*/
DEGRADE("degrade", DegradeRule.class),
/**
* param flow 热点规则
*/
PARAM_FLOW("param-flow", ParamFlowRule.class),
/**
* system 系统规则
*/
SYSTEM("system", SystemRule.class),
/**
* authority 授权规则
*/
AUTHORITY("authority", AuthorityRule.class),
/**
* gateway flow 网关限流规则
*/
GW_FLOW("gw-flow","com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule"),
/**
* api 用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合
*/
GW_API_GROUP("gw-api-group","com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition");
2. 网关流控规则
Field |
说明 |
默认值 |
resource |
网关route或自定义API分组名称(注:网关route这里的值不是route.id,可调试 |
|
resourceMode |
网关route |
|
grade |
限流阈值类型,QPS【1】或线程数【0】 |
QPS |
count |
限流阈值,QPS阈值或线程数值 |
|
intervalSec |
统计时间间隔 |
1秒 |
controlBehavior |
流控效果,目前支持快速失败【0】和匀速排队【1】 |
快速失败 |
burst |
应对突发请求时额外允许的请求数目 |
|
maxQueueingTimeoutMs |
匀速排队模式下的最长排队时间,单位毫秒,仅在匀速排队模式下生效 |
|
paramItem |
参数属性配置,parseStrategy:提取参数策略(0:Clien IP,1:Remote HOST,2:Header,3:请求参数,4:Cookie);fieldName:若提取策略是Header模式或者URL参数模式,则需要指定header名称或URL参数名称;pattern:参数值的匹配模式;matchStrategy:参数值的匹配策略,支持精确匹配,子串匹配和正则匹配。 |
Field 说明 默认值
resource 资源名称,网关route或自定义API分组名称(注:网关route这里的值不是route.id,可调试
resourceMode 限流资源类型,网关route【0】或自定义API分组【1】(详细查看GatewayFlowRule和SentinelGatewayConstants) 网关route
grade 限流阈值类型,QPS【1】或线程数【0】 QPS
count 限流阈值,QPS阈值或线程数值
intervalSec 统计时间间隔,单位秒 1秒
controlBehavior 流控效果,目前支持快速失败【0】和匀速排队【1】 快速失败
burst 应对突发请求时额外允许的请求数目
maxQueueingTimeoutMs 匀速排队模式下的最长排队时间,单位毫秒,仅在匀速排队模式下生效
paramItem 参数属性配置,parseStrategy:提取参数策略(0:Clien IP,1:Remote HOST,2:Header,3:请求参数,4:Cookie);fieldName:若提取策略是Header模式或者URL参数模式,则需要指定header名称或URL参数名称;pattern:参数值的匹配模式;matchStrategy:参数值的匹配策略,支持精确匹配,子串匹配和正则匹配。
3. 导入依赖
com.alibaba.cloud
spring-cloud-alibaba-sentinel-gateway
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
com.alibaba.csp
sentinel-datasource-nacos
4. 网关配置
先放在本地Spring Boot配置文件bootstrap-dev.yml中,后面测试通过把后再把Sentinel配置放至Nacos
spring:
cloud:
nacos:
# 注册中心
discovery:
server-addr: http://localhost:8848
# 配置中心
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
file-extension: yaml
sentinel:
enabled: true # sentinel开关
eager: true
transport:
dashboard: 192.168.1.188:8858
client-ip: localhost
datasource:
# 网关限流规则,gw-flow为key,随便定义
gw-flow:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: ${spring.application.name}-gw-flow-rules # 流控规则配置文件名:youlai-gateway-gw-flow-rules
groupId: SENTINEL_GROUP
data-type: json
rule-type: gw-flow
# 自定义API分组,gw-api-group为key,随便定义
gw-api-group:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: ${spring.application.name}-gw-api-group-rules # 流控规则配置文件名:youlai-gateway-gw-api-group-rules
groupId: SENTINEL_GROUP
data-type: json
rule-type: gw-api-group
这里解释下配置中的datasource,因为在Sentinel添加流控规则之后,如果重启服务,之前配置的规则就会消失,所以这里需要持久化Sentinel配置,从上面的配置可以看出选择的是Nacos。不过这里先别急在Nacos添加网关流控规则,下文在测试确认需求后配置。
5. 网关流控客户端标识
网关流控和普通流控有很多区别,其中网关流控类型是gw-flow,普通流控类型是flow
怎么标识流控是网关类型呢?
很多博客文章都没有着重此点,因为前阵子纠结于网关流控的面板和普通流控的面板不一致而去搜相关的资料,最后还是在Sentinel官方文档中找到此开关,就是需要在youlai-gateway网关应用添加JVM启动参数。
# 注:通过 Spring Cloud Alibaba Sentinel 自动接入的 API Gateway 整合则无需此参数
-Dcsp.sentinel.app.type=1
1
2
具体如下图:
6. 测试需求制定
在做好上面Sentinel Dashboard部署以及Spring Cloud Gateway整合Sentinel工作之后,Sentinel的两个部分(控制台和Java客户端)也就齐活了,那么接下来就进入测试。
下图简单的描述了OAuth2认证接口的流程,用户请求网关,网关(youlai-gateway)根据请求标识转发至认证中心,认证中心(youlai-auth)需要从系统服务(youlai-admin)获取数据库的用户信息再和请求携带的用户信息进行密码判读,成功则返回token给用户。
针对以上的OAuth2认证流程,提出来一个需求:
假设认证中心服务的QPS上限为10,系统服务的QPS上限为5,如何通过Sentinel实现限流控制?
**温馨提示:**留意下上图中的红线部分,你认为网关的流控是否能限制到与其间接相关的系统服务youlai-admin吗?
7. Nacos添加网关流控规则
进入Nacos控制台,添加网关流控规则,具体内容参考网关流控字段说明。
需要注意的是资源名称resource不是路由中配置的route的id,在开启Sentinel时调试SentinelGatewayFilter#filter方法可以看到自动生成的是固定前缀ReactiveCompositeDiscoveryClient_拼接应用名称${spring.application.name},所以在配置文件中一定要按照自动生成的规则配置resource的值。填写网关路由route的id,Sentinel的网关流控是无法生效的。
上面网关流控规则中,限制了认证中心youlai-auth的QPS上限为10,系统服务youlai-admin的QPS上限为5。
至于为什么这么设定,因为有个猜想需要验证,网关流控是否只能限制和直接关联的youlai-auth,而不能限制间接相关的youlai-admin。
如果通过的QPS是10,那说明网关流控不能控制间接相关youlai-admin,如果通过的QPS是5,则说明网关流控能控制间接相关的youlai-admin,至于结果,先留个悬念吧,看下文的测试结果。
已添加的网关流控规则如下图:
在Nacos添加了网关流控规则之后,会同步到Sentinel,进入Sentinel控制台查看
8. 网关流控测试
在完成上述步骤之后,接下来就进入真正的测试环节。
添加线程组
测试计划(鼠标右击)->添加->线程(用户)->线程组
因为youlai-auth的QPS处理上限为10,所以这里的线程数大于10即可看到被限制的请求
添加HTTP请求
OAuth2登录线程组(鼠标右击)->添加->取样器->HTTP请求
接口是通过网关转发到认证中心的认证接口获取token
添加察看结果树
因为要看请求的响应,所以这里添加察看结果树。
OAuth2登录线程组(鼠标右击)->添加->监听器->察看结果树
启动线程组测试
启动线程组,每秒15次认证请求,需要注意的是,如果测试计划有多个线程组,需禁用除了测试之外的其他线程组。
点击察看结果树查看请求的情况
进入Sentinel控制台,查看实时监控
可以看到1秒15次请求,因为流控设置的QPS上限是10,所以10次通过,被Sentinel拒绝了5次。
这个结果也直接说明了网关流控并不是万能的,不能限制OAuth2认证请求中与其间接相关youlai-admin的微服务,因为在网关流控设置了youlai-admin的QPS上线为5,但最后整条链路成功的却是10。既然网关流控无法应对此类场景,是否还有其他的办法来做到呢?当然有:普通流控。
9. 自定义网关流控异常
上面Sentinel限流的默认异常响应如下
{"code":429,"message":"Blocked by Sentinel: ParamFlowException"}
1
假如想自定义网关流控异常响应,该如何实现呢?
可以通过在GatewayCallbackManager上通过setBlockHandler方法注册回调实现,当请求被限流后,实现自定义的异常响应。
自定义异常代码:
@PostConstruct
private void initBlockHandler() {
BlockRequestHandler blockRequestHandler = (exchange, t) ->
ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(ResultCode.FLOW_LIMITING.toString()));
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
JMeter中查看被限流的响应,可以看到已按照自定义的响应异常返回,其中B0210 是Java开发手册上的关于系统限流的错误码