SpringCloud之Sentinel
1、Sentinel简介
随着分布式系统变得越来越流行,服务之间的可靠性变得比以往任何时候都更加重要。Sentinel是一个功能强大的流量控制组件,以“流量”为突破点,涵盖多个领域,包括流量控制,并发限制,电路中断和自适应系统保护,以保证微服务的可靠性。
Sentinel于2012年诞生于阿里巴巴,其主要目的是控制流量。2018年Sentinel发展为一个开源项目。
2、Sentinel的关键概念
资源
资源可以使任何东西,例如服务、方法、甚至代码片段。一旦被Sentinel API包装,就可以被定义为资源,可以申请Sentinel提供的保护。
规则
Sentinel保护资源的方式由规则定义,例如流量控制、并发和断路规则。规则可以动态修改,并实时生效。
3、Sentinel流量控制的原理
流量控制基于以下统计数据:
- 资源之间的调用链、例如资源的调用链路、资源与资源之间的关系
- 运行时指标、如QPS(每秒查询率)、线程池、响应时间和系统负载等
- 控制的效果、例如直接限流、冷启动、排队等。
4、熔断
熔断用于检测故障并封装防止故障、例如临时的系统外部故障等、防止在故障期间不断发生重复的逻辑调用错误。
Sentinel 和 Hystrix 的原则是一致的: 当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。
与Hystrix的区别:
Hystrix 通过线程池的方式,来对依赖(在我们的概念中对应资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本,还需要预先给各个资源做线程池大小的分配。
Sentinel实现断路的原则
4.1、控制最大并发线程
Sentinel不是使用线程池、而是通过限制并发线程数来减少不稳定资源的影响、当线程数累计到一定数量时、新传入的请求将被拒绝。等堆积的线程完成任务之后才开始继续接受请求。
通过限制并发线程,将发开从预先分配线程池大小中释放出来,同时避免了计算开销,例如排队、调度和上下文切换等。
4.2、降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的间窗口之后才重新恢复。
4.3、负载保护
Sentinel 同时对系统的维度提供保护。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
5、Sentinel的工作机制
Sentinel 的主要工作机制如下:
- 对主流框架提供适配或者显示的 API,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。
- 根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便您定义及改变规则。
- Sentinel 提供实时的监控系统,方便您快速了解目前系统的状态。
6、Sentinel限流机制详解
6.1、基于QPS/并发数的流量控制
流量控制主要有两种统计类型,一种是统计并发线程数,另外一种则是统计 QPS。类型由 FlowRule
的 grade
字段来定义。其中,0 代表根据并发数量来限流,1 代表根据 QPS 来进行流量控制。其中线程数、QPS 值,都是由 StatisticSlot
实时统计获取的。
可通过下面链接的方式来查看实时的统计信息:
http://localhost:{port}/cnode?id=resourceName
其中包含的字段:
- thread: 代表当前处理该资源的线程数;
- pass: 代表一秒内到来到的请求;
- blocked: 代表一秒内被流量控制的请求数量;
- success: 代表一秒内成功处理完的请求;
- total: 代表到一秒内到来的请求以及被阻止的请求总和;
- RT: 代表一秒内该资源的平均响应时间;
- 1m-pass: 则是一分钟内到来的请求;
- 1m-block: 则是一分钟内被阻止的请求;
- 1m-all: 则是一分钟内到来的请求和被阻止的请求的总和;
- exception: 则是一秒内业务本身异常的总和。
6.1.1 并发线程数流量控制
并发线程数限流用于保护业务线程数不被耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发线程数限流不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目,如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。
6.1.2 QPS流量控制
当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的手段包括以下几种:直接拒绝、Warm Up、匀速排队。对应 FlowRule
中的 controlBehavior
字段。
直接拒绝
直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT
)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException
。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
Warm Up
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP
)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮
匀速排队
匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
6.2、基于调用关系的流量控制
调用关系包括调用方、被调用方;一个方法又可能会调用其它方法,形成一个调用链路的层次关系。Sentinel 通过 NodeSelectorSlot
建立不同资源间的调用的关系,并且通过 ClusterNodeBuilderSlot
记录每个资源的实时统计信息。
基于调用链路的统计信息,衍生出多种流量控制手段
6.2.1 根据调用方限流
ContextUtil.enter(resourceName, origin)
方法中的 origin
参数标明了调用方身份。这些信息会在 ClusterBuilderSlot
中被统计。可通过以下url来展示不同的调用方对同一个资源的调用数据:
http://localhost:8719/origin?id=nodeA
流控规则中的 limitApp
字段用于根据调用来源进行流量控制。该字段的值有以下三种选项,分别对应不同的场景:
default
:表示不区分调用者,来自任何调用者的请求都将进行限流统计。如果这个资源名的调用总和超过了这条规则定义的阈值,则触发限流。{some_origin_name}
:表示针对特定的调用者,只有来自这个调用者的请求才会进行流量控制。例如NodeA
配置了一条针对调用者caller1
的规则,那么当且仅当来自caller1
对NodeA
的请求才会触发流量控制。other
:表示针对除{some_origin_name}
以外的其余调用方的流量进行流量控制。例如,资源NodeA
配置了一条针对调用者caller1
的限流规则,同时又配置了一条调用者为other
的规则,那么任意来自非caller1
对NodeA
的调用,都不能超过other
这条规则定义的阈值。
同一个资源名可以配置多条规则,规则的生效顺序为:> other > default
6.2.2 根据调用链路入口限流
NodeSelectorSlot
中记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一棵调用树。这棵树的根节点是一个名字为 machine-root
的虚拟节点,调用链的入口都是这个虚节点的子节点。
一棵典型的调用树如下图所示:
machine-root
/ \
/ \
Entrance1 Entrance2
/ \
/ \
DefaultNode(nodeA) DefaultNode(nodeA)
上图中来自入口 Entrance1
和 Entrance2
的请求都调用到了资源 NodeA
,Sentinel 允许只根据某个入口的统计信息对资源限流。比如我们可以设置 FlowRule.strategy
为 RuleConstant.CHAIN
,同时设置 FlowRule.ref_identity
为 Entrance1
来表示只有从入口 Entrance1
的调用才会记录到 NodeA
的限流统计当中,而不关心经 Entrance2
到来的调用。
调用链的入口(上下文)是通过 API 方法 ContextUtil.enter(contextName)
定义的,其中 contextName 即对应调用链路入口名称。
6.2.3 具有关系的资源流量控制:关联流量控制
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db
和 write_db
这两个资源分别代表数据库读写,我们可以给 read_db
设置限流规则来达到写优先的目的:设置 FlowRule.strategy
为 RuleConstant.RELATE
同时设置 FlowRule.ref_identity
为 write_db
。这样当写库操作过于频繁时,读数据的请求会被限流。
7、降级限流策略
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException
)。
Sentinel通常以下面几种方式来衡量资源是否处于稳定的状态:
- 平均响应时间 (
DEGRADE_GRADE_RT
):当资源的平均响应时间超过阈值(DegradeRule
中的count
,以 ms 为单位)之后,资源进入准降级状态。如果接下来 1s 内持续进入 5 个请求(即 QPS >= 5),它们的 RT 都持续超过这个阈值,那么在接下的时间窗口(DegradeRule
中的timeWindow
,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出DegradeException
)。注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx
来配置。 - 异常比例 (
DEGRADE_GRADE_EXCEPTION_RATIO
):当资源的每秒请求量 >= 5,并且每秒异常总数占通过量的比值超过阈值(DegradeRule
中的count
)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule
中的timeWindow
,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。 - 异常数 (
DEGRADE_GRADE_EXCEPTION_COUNT
):当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若timeWindow
小于 60s,则结束熔断状态后仍可能再进入熔断状态。
注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException
)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex)
记录业务异常。
8、网关限流
Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。
Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑:
GatewayFlowRule
:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。- ApiDefinition
:用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫
my_api,请求 path 模式为
/foo/和
/baz/的都归到
my_api` 这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。
其中网关限流规则 GatewayFlowRule
的字段解释如下:
- resource`:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。
resourceMode
:规则是针对 API Gateway 的 route(RESOURCE_MODE_ROUTE_ID
)还是用户在 Sentinel 中定义的 API 分组(RESOURCE_MODE_CUSTOM_API_NAME
),默认是 route。grade
:限流指标维度,同限流规则的grade
字段。count
:限流阈值intervalSec
:统计时间窗口,单位是秒,默认是 1 秒。controlBehavior
:流量整形的控制效果,同限流规则的controlBehavior
字段,目前支持快速失败和匀速排队两种模式,默认是快速失败。burst
:应对突发请求时额外允许的请求数目。maxQueueingTimeoutMs
:匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。paramItem
:参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换成热点规则。其中的字段:parseStrategy
:从请求中提取参数的策略,目前支持提取来源 IP(PARAM_PARSE_STRATEGY_CLIENT_IP
)、Host(PARAM_PARSE_STRATEGY_HOST
)、任意 Header(PARAM_PARSE_STRATEGY_HEADER
)和任意 URL 参数(PARAM_PARSE_STRATEGY_URL_PARAM
)四种模式。fieldName
:若提取策略选择 Header 模式或 URL 参数模式,则需要指定对应的 header 名称或 URL 参数名称。pattern
:参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控;若为空则统计该请求属性的所有值。(1.6.2 版本开始支持)matchStrategy
:参数值的匹配策略,目前支持精确匹配(PARAM_MATCH_STRATEGY_EXACT
)、子串匹配(PARAM_MATCH_STRATEGY_CONTAINS
)和正则匹配(PARAM_MATCH_STRATEGY_REGEX
)。(1.6.2 版本开始支持)
用户可以通过 GatewayRuleManager.loadRules(rules)
手动加载网关规则,或通过 GatewayRuleManager.register2Property(property)
注册动态规则源动态推送(推荐方式)。
8.1 Spring Cloud Gateway
从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:
- route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
- 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
使用时只需注入对应的 SentinelGatewayFilter
实例以及 SentinelGatewayBlockExceptionHandler
实例即可。
8.2 Zuul
Sentinel 提供了 Zuul 1.x 的适配模块,可以为 Zuul Gateway 提供两种资源维度的限流:
- route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 route ID(对应
RequestContext
中的proxy
字段) - 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
8.3 网关流控的实现原理
当通过 GatewayRuleManager
加载网关流控规则(GatewayFlowRule
)时,无论是否针对请求属性进行限流,Sentinel 底层都会将网关流控规则转化为热点参数规则(ParamFlowRule
),存储在 GatewayRuleManager
中,与正常的热点参数规则相隔离。转换时 Sentinel 会根据请求属性配置,为网关流控规则设置参数索引(idx
),并同步到生成的热点参数规则中。
外部请求进入 API Gateway 时会经过 Sentinel 实现的 filter,其中会依次进行 路由/API 分组匹配、请求属性解析和参数组装。Sentinel 会根据配置的网关流控规则来解析请求属性,并依照参数索引顺序组装参数数组,最终传入 SphU.entry(res, args)
中。Sentinel API Gateway Adapter Common 模块向 Slot Chain 中添加了一个 GatewayFlowSlot
,专门用来做网关规则的检查。GatewayFlowSlot
会从 GatewayRuleManager
中提取生成的热点参数规则,根据传入的参数依次进行规则检查。若某条规则不针对请求属性,则会在参数最后一个位置置入预设的常量,达到普通流控的效果。
9、@SentinelResource注解
@SentinelResource
用于定义资源,并提供可选的异常处理和 fallback 配置项。 @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 函数,否则无法解析。
-
defaultFallback
(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore
里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:- 返回值类型必须与原函数返回值类型一致;
- 方法参数列表需要为空,或者可以额外多一个
Throwable
类型的参数用于接收对应的异常。 - defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
fallbackClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析。
-
exceptionsToIgnore
(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出
BlockException
时只会进入blockHandler
处理逻辑。若未配置blockHandler
、fallback
和defaultFallback
,则被限流降级时会将BlockException
直接抛出。示例:
public class TestService {
// 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 static 函数.
@SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
public void test() {
System.out.println("Test");
}
// 原函数
@SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
public String hello(long s) {
return String.format("Hello at %d", s);
}
// Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
public String helloFallback(long s) {
return String.format("Halooooo %d", s);
}
// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
public String exceptionHandler(long s, BlockException ex) {
// Do some log here.
ex.printStackTrace();
return "Oops, error occurred at " + s;
}
}
Spring AOP
若您的应用使用了 Spring AOP,您需要通过配置的方式将 SentinelResourceAspect
注册为一个 Spring Bean:
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
10、Sentinel和Hystrix的对比
Sentinel | Hystrix | |
---|---|---|
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于响应时间或失败比率 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 基于 QPS,支持基于调用关系的限流 | 有限的支持 |
流量整形 | 支持慢启动、匀速器模式 | 不支持 |
系统负载保护 | 支持 | 不支持 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架的适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |
11、在生产环境中使用Sentinel
11.1 规则管理及推送
推送模式 | 说明 | 优点 | 缺点 |
---|---|---|---|
原始模式 | API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource ) | 简单,无任何依赖 | 不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境 |
Pull模式 | 扩展写数据源(WritableDataSource ), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等 | 简单,无任何依赖;规则持久化 | 不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。 |
Push模式 | 扩展读数据源(ReadableDataSource ),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。 | 规则持久化;一致性;快速 | 引入第三方依赖 |
原始模式
如果不做任何修改,Dashboard的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中:
这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境。
Pull模式
pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry
中。以本地文件数据源为例:
public class FileDataSourceInit implements InitFunc {
@Override
public void init() throws Exception {
String flowRulePath = "xxx";
ReadableDataSource<String, List<FlowRule>> ds = new FileRefreshableDataSource<>(
flowRulePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {})
);
// 将可读数据源注册至 FlowRuleManager.
FlowRuleManager.register2Property(ds.getProperty());
WritableDataSource<List<FlowRule>> wds = new FileWritableDataSource<>(flowRulePath, this::encodeJson);
// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.
// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.
WritableDataSourceRegistry.registerFlowDataSource(wds);
}
private <T> String encodeJson(T t) {
return JSON.toJSONString(t);
}
}
本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。以本地文件数据源为例,推送过程如下图所示:
首先 Sentinel 控制台通过 API 将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。使用 pull 模式的数据源时一般不需要对 Sentinel 控制台进行改造。
这种实现方法好处是简单,不引入新的依赖,坏处是无法保证监控数据的一致性
Push模式
生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了:
我们提供了 ZooKeeper, Apollo, Nacos 等的动态数据源实现。以 ZooKeeper 为例子,如果要使用第三方的配置中心作为配置管理,您需要做下面的几件事情:
- 实现一个公共的 ZooKeeper 客户端用于推送规则,在 Sentinel 控制台配置项中需要指定 ZooKeeper 的地址,启动时即创建 ZooKeeper Client。
- 我们需要针对每个应用(appName),每种规则设置不同的 path(可随时修改);或者约定大于配置(如 path 的模式统一为
/sentinel_rules/{appName}/{ruleType}
,e.g.sentinel_rules/appA/flowRule
)。 - 规则配置页需要进行相应的改造,直接针对应用维度进行规则配置;修改同个应用多个资源的规则时可以批量进行推送,也可以分别推送。Sentinel 控制台将规则缓存在内存中(如
InMemFlowRuleStore
),可以对其进行改造使其支持应用维度的规则缓存(key 为 appName),每次添加/修改/删除规则都先更新内存中的规则缓存,然后需要推送的时候从规则缓存中获取全量规则,然后通过上面实现的 Client 将规则推送到 ZooKeeper 即可。 - 应用客户端需要注册对应的读数据源以监听变更。
从 Sentinel 1.4.0 开始,Sentinel 控制台提供 DynamicRulePublisher
和 DynamicRuleProvider
接口用于实现应用维度的规则推送和拉取,并提供了相关的示例。Sentinel 提供应用维度规则推送的示例页面(/v2/flow
),用户改造控制台对接配置中心后可直接通过 v2 页面推送规则至配置中心。
11.2 监控
Sentinel 会记录资源访问的秒级数据(若没有访问则不进行记录)并保存在本地日志中。Sentinel 控制台可以通过 Sentinel 客户端预留的 HTTP API 从秒级监控日志中拉取监控数据,并进行聚合。
目前 Sentinel 控制台中监控数据聚合后直接存在内存中,未进行持久化,且仅保留最近 5 分钟的监控数据。若需要监控数据持久化的功能,可以自行扩展实现 MetricsRepository
接口(0.2.0 版本),然后注册成 Spring Bean 并在相应位置通过 @Qualifier
注解指定对应的 bean name 即可。MetricsRepository
接口定义了以下功能:
save
与saveAll
:存储对应的监控数据queryByAppAndResourceBetween
:查询某段时间内的某个应用的某个资源的监控数据listResourcesOfApp
:查询某个应用下的所有资源
其中默认的监控数据类型为 MetricEntity
,包含应用名称、时间戳、资源名称、异常数、请求通过数、请求拒绝数、平均响应时间等信息。
同时用户可以自行进行扩展,适配 Grafana 等可视化平台,以便将监控数据更好地进行可视化。
对于监控数据的存储,用户需要根据自己的存储精度,来考虑如何存储这些监控数据。