# Sentinel
# 概述
- 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点, 从流量控制、熔断降级、系统自适应保护等多个维度来保障微服务的稳定性。
- Sentinel 目前提供 Java、Go、C++ 等语言的原生支持。
- Sentinel 提供Dashboard,支持规则配置持久化。
- 官网文档:https://sentinelguard.io/zh-cn/docs/introduction.html (opens new window)
# 基本概念
# 资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容, 例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务, 甚至可以是一段代码。
# 规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
# 控制台
- Sentinel-Dashboard控制台是一个SpringBoot项目,已做持久化改造,可以将规则配置持久化到Nacos中,支持SpringBoot方式启动和打包启动
- 官网文档:https://sentinelguard.io/zh-cn/docs/dashboard.html (opens new window)
# 下载安装包
请下载HOS持久化版本sentinel-dashboard
做为控制台,开源版本不包含持久化功能,重启后所有规则都会丢失。
持久化版本下载地址:http://119.255.194.80/hos/install/hos-base/-/blob/master/sentinel/hos-sentinel-dashboard-1.8.6.jar
用户名/密码: hosuser/99ahivPJt
# 配置项
# nacos服务器地址
sentinel.datasource.nacos.server-addr=127.0.0.1:8848
# 存储sentinel规则的命名空间
sentinel.datasource.nacos.namespace=sentinel
# nacos服务的用户,需要有配置的命名空间权限
sentinel.datasource.nacos.username=sentinel
sentinel.datasource.nacos.password=sentinel1234
# 启动端口、服务名等
server.port=8080
spring.application.name=hos-sentinel-dashboard-starter
debug=true
# 实时监控
实时监控汇总资源信息,同时,同一个服务下的所有机器的簇点信息会被汇总,并且秒级地展示在"实时监控"下。
# 簇点链路
- 簇点链路中显示刚刚调用的资源,簇点链路页面实时的去拉取指定客户端资源的运行情况。 它一共提供两种展示模式:一种用树状结构展示资源的调用链路,另外一种则不区分调用链路展示资源的运行情况。
- 簇点链路页面可以快捷的创建流控、熔断、热点、授权规则
注意:簇点监控是内存态的信息,它仅展示启动后调用过的资源。
# 流控规则
单机阀值:表示触发规则的上限值
流控模式
- 直接:接口达到限流条件时,直接限流
- 关联:当关联的资源达到阈值时,就限流自己
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就可以限流),即当从某个接口过来的资源达到限流条件时,开启限流;
流控效果
- 快速失败:直接失败,就异常
- Warm Up:达到峰值的启动时间。当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。 即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。 即 warm ups 时间
- 排队等待:让请求以均匀的请求速度通过,阈值类型必须为QPS,否则无效请求达到阈值时,执行等待,等待时间为设置的超时时间
集群模式
- 单机均摊:每个单机的上限
- 总体阀值:所有机器的上限
# 熔断规则
某个资源出现不稳定状态(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败 避免影响到其他的资源而导致级联错误。
熔断策略 - 慢调用比例
当统计时长内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内 请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个 请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 最大RT:最大响应时间,表示请求接口内部的处理时间,请求的响应时间大于该值则统计为慢调用
- 比例阈值:慢调用的百分比,0 到 1 的范围。
熔断策略 - 异常比例
当统计时长内请求数目大于设置的最小请求数目,并且异常总数超过 异常数 时,资源进入降级状态
熔断策略 - 异常数
当统计时长内请求数目大于设置的最小请求数目,并且异常总数超过 异常数 时,资源进入降级状态
# 热点规则
- 使用QPS 模式对参数进行限制。
- 参数索引:表示请求参数是第几个,不是参数实际值,是参数在所有参数的索引位置,从0开始
# 系统规则
- 系统保护规则是从应用级别的入口流量进行控制,系统保护规则是应用整体维度的,而不是资源维度的。
- 系统规则支持一下的模式:
Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load(1分钟平均负载) 作为启发指标,进行自适应系统保护。 当系统 load(1分钟平均负载) 超过设定的启发值(阈值),且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。 系统容量由系统的 maxQps(秒级统计的最大QPS) * minRt(秒级统计的最小响应时间) 估算得出。设定参考值一般是 CPU cores * 2.5。
CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
# 授权规则
- 授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。
- 白名单:来源(origin)在白名单内的调用者允许访问
- 黑名单:来源(origin)在黑名单内的调用者不允许访问
# 集群流控
- 某个sentinel客户端采用集群的访问部署,在集群流控中,可以选择某台机器作为server端,用于流控统计并与其他机器进行交互。
- 集群流控中共有两种身份:
- Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
- Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
# 机器列表
显示某个sentinel客户端所有的机器信息
# 网关规则
- 网关规则有流控、熔断、系统三种规则类型,这里主要介绍网络流控规则,其他两种规则与非网关的流控、熔断规则一样。
- 网关提供两种资源维度的限流
- route维度:即在配置文件中配置的路由条目,资源名为对应的 routeId,这种属于粗粒度的限流,一般是对某个微服务进行限流。
- 自定义API维度:用户可以利用 Sentinel 提供的API来自定义一些API分组,这种属于细粒度的限流,针对某一类的uri进行匹配限流,可以跨多个微服务。
# 网关流控规则
- 以routeId限流时,直接在 请求链路 里选择某个routeId即可,设置类似 流控规则
- 以API分组限流时,需要先创建API分组(API管理 中 创建分组,创建分组时,可以选择精确、模糊、正则匹配3种方式)
在流控规则里使用API分组的模式进行限流,选择创建的API分组即可。
API分组:
用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。 比如我们可以定义一个 API 叫 my_api,请求 path 模式为 /foo/** 和 /baz/** 的都 归到 my_api 这个 API 分组下面。 限流的时候可以针对这个自定义的 API 分组维度进行 限流
间隔:是指可以设置QPS的秒数,当间隔设置成2时,此时表示2秒内执行N次。 burst size: 表示宽容次数,如设置1,表示在一秒内,可以访问3次,3次以上则控流。
# 客户端配置
项目使用Sentinel进行流量控制时,需要引入hos-framework-sentinel-starter
,版本由HOS版本统一控制,此时应用程序可以看作是一个Sentinel的客户端程序。
# 引入依赖
<dependency>
<groupId>com.mediway.hos</groupId>
<artifactId>hos-framework-sentinel-starter</artifactId>
</dependency>
# 配置项
集成sentinel的服务需要在配置文件中配置sentinel-dashboard地址、存储规则的Nacos信息和sentinel规则的类型信息。以下配置需要在有效的项目配置文件中添加。
# yml配置文件
spring:
cloud:
# sentinel 相关配置
sentinel:
transport:
# sentinel-dashboard 控制台地址
dashboard: 127.0.0.1:8080
# 本应用(sentinel应用客户端)与 sentinel-dashboard 的交互端口,默认8719,如果被占用会自动加1
port: 8719
# 本应用监听nacos上的sentinel规则
datasource:
# 流控--dsl-flow允许自定义,没有固定要求,主要是里面的配置
dsl-flow:
# 持久化规则,从nacos中获取
nacos:
# nacos 地址,默认和项目Nacos配置中心一致
server-addr: ${spring.cloud.nacos.config.server-addr}
# nacos用户名,必须有下面namespace命名空间的只读权限,这需要在nacos上配置权限
username: ${spring.cloud.nacos.config.username}
password: ${spring.cloud.nacos.config.password}
# 持久化规则文件在nacos上的命名空间,要和sentinel-dashboard配置一致
namespace: ${ENV_DIS_NAMESPACE:sentinel}
# 持久化规则文件在nacos上的分组,不可修改,这是固定的
groupId: SENTINEL_GROUP
# 持久化规则文件在nacos上的名称,必须是应用名-flow-rules
dataId: ${spring.application.name}-flow-rules
# 持久化规则文件在nacos上的文件格式
data-type: json
# 规则类型 flow-流控控制
rule-type: flow
# 熔断降级
dsl-degrade:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
username: ${spring.cloud.nacos.config.username}
password: ${spring.cloud.nacos.config.password}
namespace: ${ENV_DIS_NAMESPACE:sentinel}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-degrade-rules
data-type: json
rule-type: degrade
# 热点规则
dsl-param-flow:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
username: ${spring.cloud.nacos.config.username}
password: ${spring.cloud.nacos.config.password}
namespace: ${ENV_DIS_NAMESPACE:sentinel}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-param-flow-rules
data-type: json
rule-type: param-flow
# 系统规则
dsl-system:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
username: ${spring.cloud.nacos.config.username}
password: ${spring.cloud.nacos.config.password}
namespace: ${ENV_DIS_NAMESPACE:sentinel}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-system-rules
data-type: json
rule-type: system
# 授权规则
dsl-authority:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
username: ${spring.cloud.nacos.config.username}
password: ${spring.cloud.nacos.config.password}
namespace: ${ENV_DIS_NAMESPACE:sentinel}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-authority-rules
data-type: json
rule-type: authority
- dataId 以当前服务名称${spring.application.name}-后缀组成,后缀固定,分别为 流控规则(flow-rules)、熔断规则(degrade-rules)、热点规则(param-flow-rules)、 系统规则(system-rules)、授权规则(authority-rules)
- rule-type 固定,具体可以查看 com.alibaba.cloud.sentinel.datasource.RuleType 类
# 网关yml配置文件
spring:
cloud:
# 网关配置
gateway:
enabled: true
discovery:
locator:
lower-case-service-id: true
routes:
- id: mamagecenter-gateway-provider
uri: lb://hos-sentinel-service
predicates:
- Path=/sentinel/gateway/{value}
# sentinel 相关配置
sentinel:
transport:
# sentinel-dashboard 控制台地址
dashboard: 127.0.0.1:8080
# 本应用(sentinel应用客户端)与 sentinel-dashboard 的交互端口,默认8719,如果被占用会自动加1
port: 8719
# 本应用监听nacos上的sentinel规则
datasource:
# 网关routeId规则
dsl-gateway-flow:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
username: ${spring.cloud.nacos.config.username}
password: ${spring.cloud.nacos.config.password}
namespace: ${ENV_DIS_NAMESPACE:sentinel}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-gateway-flow-rules
data-type: json
rule-type: gw-flow
# 网关API分组规则
dsl-gateway-api:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
username: ${spring.cloud.nacos.config.username}
password: ${spring.cloud.nacos.config.password}
namespace: ${ENV_DIS_NAMESPACE:sentinel}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-gateway-api-rules
data-type: json
rule-type: gw-api-group
- dataId 以当前服务名称${spring.application.name}-后缀组成,后缀固定,分别为 网关RouteId(gateway-flow-rules)、网关API分组(gateway-api-rules)
- rule-type 固定,具体可以查看 com.alibaba.cloud.sentinel.datasource.RuleType 类
- 注意:spring.cloud.sentinel.eager 属性,如果设置成true,会在网关请求之前就会加到控制台,会展示成非网关的客户端页面
# 使用示例
示例较为简单,主要在规则配置以及测试触发方面,这里主要展示Sentinel资源设置以及 触发控制规则后的统一异常处理。
@GetMapping("/sentinel/system")
public String sentinelSystem() {
return "默认请求接口即为限流埋点===sentinel规则 ==== now time:" + LocalDateTime.now();
}
Sentinel 默认为所有的 HTTP 服务提供了限流埋点。引入依赖后自动完成所有埋点。只需要再控制配置限流规则即可
// 原本的业务方法.
@SentinelResource(blockHandler = "blockHandlerForGetUser")
public User getUserById(String id) {
throw new RuntimeException("getUserById command failed");
}
// blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用
public User blockHandlerForGetUser(String id, BlockException ex) {
return new User("admin");
}
使用@SentinelResource注解对某个特定的方法进行限流或降级处理
# 统一异常处理
限流资源不使用@SentinelResource则所有的满足限流规则的资源都会通过自定义业务统一处理,hos-framework-sentinel-starter
已经提供了默认的统一异常处理,可以直接使用。
如果不满足业务需求,需要业务自定义统一处理,根据本身业务自定义流控异常统一处理,可以参考此类,主要是需要实现BlockExceptionHandler接口,重写handle方法。
/**
* 自定义接口限流处理
*/
@Component
public class CustomBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws Exception {
JSONObject resultObj = new JSONObject();
if (ex instanceof FlowException) {
resultObj.put("code", 100);
resultObj.put("msg", "BlockExceptionHandler==接口限流");
}
if (ex instanceof DegradeException) {
resultObj.put("code", 101);
resultObj.put("msg", "BlockExceptionHandler==服务降级");
}
if (ex instanceof ParamFlowException) {
resultObj.put("code", 102);
resultObj.put("msg", "BlockExceptionHandler==热点参数限流");
}
if (ex instanceof SystemBlockException) {
resultObj.put("code", 103);
resultObj.put("msg", "BlockExceptionHandler==触发系统保护规则");
}
if (ex instanceof AuthorityException) {
resultObj.put("code", 104);
resultObj.put("msg", "BlockExceptionHandler==授权规则不通过");
}
BaseResponse baseResponse = BaseResponse.error(resultObj.getString("code"), resultObj.getString("msg"));
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
ObjectMapper objectMapper = new ObjectMapper();
response.getWriter().println(objectMapper.writeValueAsString(baseResponse));
}
}
# fallback
- 资源使用@SentinelResource标识,并设置限流处理fallback参数。
- fallback:针对Java本身出现的异常进行处理的对应属性。
- 自定义fallback函数,必须与资源方法具有相同的参数,可以额外多一个Throwable参数。
- 自定义fallback函数,如果不与资源方法在同一个类中,必须定义成static。
// fallback:此方法异常(非限流异常)会调用 AnnotationFallbackHandlerException#fallBackA方法
@GetMapping("/fallback")
@SentinelResource(fallback = "fallBackA", fallbackClass = AnnotationFallbackHandlerException.class)
public String testFallback() {
return "异常统一处理接口 === fallback ==== now time:" + LocalDateTime.now();
}
/**
* 在 @SentinelResource配置fallback、fallbackClass 值
* 方法必须是 static
*/
public class AnnotationFallbackHandlerException {
// fallback函数,必须与资源方法入参一样,可以额外多一个Throwable
public static String fallBackA(Throwable exception) {
return "AnnotationFallbackHandlerException ==== 全局(fallBackA)异常 === :" + exception.getMessage();
}
}
# blockHandler
- 资源使用@SentinelResource标识,并设置限流处理blockHandler参数。
- blockHandler:针对违反Sentinel控制台配置规则时触发BlockException异常时对应处理的属性。
- 自定义blockHandler函数,必须与资源方法具有相同的参数、返回类型,可以额外多一个BlockException参数。
- 自定义blockHandlerk函数,如果不与资源方法在同一个类中,必须定义成static。
@GetMapping("/blockHandler")
@SentinelResource(blockHandler = "handlerExceptionA", blockHandlerClass = {AnnotationBlockHandlerException.class})
public String testBlockHandler() {
return "异常统一处理接口 === blockHandler ==== now time:" + LocalDateTime.now();
}
/**
* 在 @SentinelResource配置blockHandler、blockHandlerClass 值
* 方法必须是 static
*/
public class AnnotationBlockHandlerException {
/**
* blockHandler函数, 返回类型和参数必须与原函数返回类型和参数一致,可以额外多一个BlockException
*/
public static String handlerExceptionA(BlockException exception){
return "AnnotationBlockHandlerException ==== 全局(handlerExceptionA)异常 === :" + exception.getMessage();
}
}
# 授权规则
- 使用授权规则时, 可以配置请求来源,从而定义不同的请求来源的应用名,便于规则配置。
- 示例以从请求头header中获取origin,实际需要根据业务自定义
- 需要实现RequestOriginParser接口
@Component
public class AuthorityRequestOriginParser implements RequestOriginParser {
// 授权规则---请求来源处理
@Override
public String parseOrigin(HttpServletRequest request) {
// 1.获取请求头
String origin = request.getHeader("origin");
// 2.非空判断
if (StringUtils.isEmpty(origin)) {
origin = "testAuth";
}
return origin;
}
}