MSE 流量防护将应用作为防护主体,以流量为切入点,借助接口流控、并发隔离、熔断规则、热点参数防护以及系统防护等功能,为应用的运行时提供流量不稳定场景下的保障。它是阿里巴巴双十一技术体系中的核心组件,同时也是开源框架Sentinel的商业化产品,本文介绍MSE 流量防护使用指南。
基本概念
资源:MSE 流量防护功能中的基本概念,也就是限流生效的基本对象,它可以是 Java 应用程序中的任何内容。MSE 会为常见的框架自动定义资源(对应于控制台接口详情页面处的WEB服务以及RPC服务),用户也可以通过编写代码的方式来定义资源(对应于控制台接口详情页面处的自定义接口)。
规则:围绕资源的实时状态设定的规则,包括接口流控、并发隔离、熔断规则、热点参数防护以及系统防护。所有规则都可以动态实时调整。
并发:在 MSE 流量防护中特指在途请求数,即特定时刻已经开始处理但是还没完成处理的请求数。
设计理念和功能简介
现代系统架构往往是复杂的,但对于系统的稳定性风险,我们需要关注的主要是三个方面的变化,分别是:逻辑、资源、流量。其中逻辑变化带来的风险由质量保障/变更流程来收敛,资源变化带来的风险由基础设施来收敛,而流量变化带来的风险则需要由流量防护来保障。
在复杂的系统架构以及业务发展下,流量是多变且不可预测的,所以从整体架构上我们需要在每一层都进行限流,在保障保护效果的基础上,进一步增强系统的稳定性并减少无效请求带来的整体系统损耗。
但从每一个应用的视角来看,流量可以分为上游调用我的流量(服务端)和我调用下游应用的流量(客户端)。两类流量的变化都会对应用提供的服务带来稳定性风险。
不确定的入口流量
代表场景:重大活动,热点事件等。
首先是其他应用调用我的流量,这种流量是不确定的,在一些场景下,流量可能会翻数倍,远远超过应用所能承受的容量上限,这就会导致所有请求RT暴涨、系统宕机,影响服务的整体可用性。对于这种场景,我们可以通过接口流控、并发隔离、热点参数以及系统防护的服务端防护等功能,将超出应用处理能力的请求快速失败,不再经由业务逻辑,避免无端消耗,保障应用有资源来处理容量内的请求。
不稳定的出口流量
代表场景:第三方算法服务,第三方支付服务等。
还有一类流量是我调用其他应用/第三方服务的请求,这种流量往往具有不稳定性,请求的质量不稳定,可能存在突发的大量异常或者慢调用。当这种情况发生时,如果我们没有任何的防护措施的话我们的应用很有可能会因为没法正确处理异常导致服务质量下降,或者因为慢调用导致自身请求堆积,线程等资源耗尽无法正常处理任何请求。同时对于这种场景,我们可以通过熔断规则、并发隔离以及系统防护的客户端防护等功能来降级或者限制这些调用,使得应用在这种场景下也能够留有一定的资源来处理正常的请求。
实践指南
应用接入
接入MSE微服务治理中心可参见应用接入。
规则配置
这部分我们按照前述的两个大场景来分别描述:
不确定的入口流量(服务端)
不稳定的出口流量(客户端)
不确定的入口流量
不确定的入口流量主要需要配置接口流控、并发隔离、热点参数以及系统防护的服务端防护等规则。按照阈值评估-> 配置系统防护(服务端防护)-> 配置流量防护 -> 优化限流行为的步骤来进行。
阈值评估
在通常的实践中我们一般会根据场景的不同来决定阈值评估的方式,主要分为以下两种:
预期的高峰流量场景(活动场景):往往配合压测来进行阈值评估。
日常场景:推荐配合压测来进行阈值评估,但也可以依靠历史数据。
预期的高峰流量场景(活动场景)
在这个场景下,我们往往对高峰时我们的应用需要正常处理多少请求有一个预先的评估,并根据这个评估规划了资源的扩容和分配。通过多轮的压测和调整(往往会使用不同的流量调用模型多次压测),我们能够定下一套资源使得我们的整体系统在接受预期流量的同时将负载保持在一个接近满载但是相对安全的水位。接下来我们就需要根据此时压测流量来配置各个应用以及接口的规则阈值,从而让我们的应用在实际高峰场景下即便面对数倍于系统能力的突发流量也能够正常处理预期的流量,达到业务目标。
日常场景
有些应用并不存在明显的预期中的高峰流量场景,但也存在被突发流量打垮的风险。对于这种应用我们优先推荐通过压测的方式逐步摸高,确定当前系统的容量上限(超过该流量系统的服务质量出现明显下滑),并且根据这个上限来配置应用和接口的阈值,避免突发流量打垮应用。除此之外,也会有部分应用不具备压测的条件,无法确定容量的上限,此时我们可以根据日常的流量指标(往往是最大流量)结合当时系统负载水位适当地扩大1.5倍左右作为阈值来配置规则,比如日常最大流量为1000QPS,此时CPU等系统负载水位在30%,我们就可以考虑配置1500QPS。这样,虽然我们可能在一些突发流量场景下会触发一些限流,但是能够保障系统持续处于可用状态。
配置系统防护(服务端防护)
完成了阈值评估之后,我们可以先来配置系统防护。系统防护提供了在不同场景下的节点维度的流量防护能力,以应对各种预期外的情况。用户可以根据应用的实际情况选择不同的功能,可以同时使用各个功能。简单来说如下:
CPU相关型应用 -> 自适应过载保护
系统负载流量相关 -> 总QPS限流
系统负载流量相关(RT普遍秒级别) -> 总并发限流
自适应过载保护
简介
自适应过载保护将CPU使用率作为衡量系统负载的依据,引入控制论的算法自适应地调整对服务端流量的限流比例,在预期外的流量突增场景下也能将CPU使用率相对平稳地控制在配置的阈值范围内。
生效范围
自适应过载保护对所有服务端接口生效,优先级低于流量防护规则。
适用场景
自适应过载保护为服务端接口提供基于CPU的兜底防护,适用于CPU相关型应用,预期外的接口出现突增 -> 系统CPU使用率持续上升 -> 影响核心接口RT。
页面说明
页面左侧为自适应过载保护事件列表,右侧展示近5分钟该应用的节点平均CPU使用率变化趋势。指标每10s上报一次,实际运行时为了保障足够的灵敏,每500ms刷新一次CPU使用率,所以可能存在指标上没超阈值,但实际存在部分时刻超过阈值,建议以事件信息为准。
事件为节点维度,基于算法的状态变更,包含限流开始事件、限流持续事件以及限流结束事件。
单击事件操作的查看链接,可以查询对应IP节点的CPU使用率数据,并将时间回放至事件上报时间,以观察事件触发时对应节点的CPU使用率以及限流概率等信息。
配置项 | 描述 |
开启状态 |
|
CPU使用率 | 定义预期的CPU使用率阈值,自适应过载保护会基于系统实际的CPU使用率以及配置的CPU使用率阈值结合算法自适应地调整接口限流的概率,帮助系统在高压场景下通过拒绝一部分请求的方式,维持CPU使用率在配置的阈值上下小范围波动。 |
例外设置 | 详见例外项。 |
总QPS限流
简介
总QPS限流将以节点维度统计总QPS(单节点所有服务端接口QPS之和),对于超过阈值的请求进行限流操作。
生效范围
总QPS限流对所有服务端接口生效,优先级低于流量防护规则。
适用场景
由于不是所有系统的表现都与CPU强相关,有些应用在低CPU负载下也会因为内存、网络等原因导致性能劣化。而总QPS限流是基于节点的总QPS来进行限流,提供了基于流量的防护手段。
预期外的接口出现突增 -> 竞争资源紧缺 -> 影响核心接口。
页面说明
页面左侧为总QPS限流事件,右侧展示近5分钟该应用的节点平均总QPS请求数据变化趋势,事件为节点和接口维度,基于实际发生总QPS限流的节点以及接口上报事件,每5分钟上报一次,上报过去5分钟内发生的限流事件。
单击事件的查看链接,可以查询对应IP节点的总QPS,并将时间回放至事件上报时间附近,以观察事件触发时对应节点的总QPS以及限流是否符合预期,如果需要查看更加详细的信息,比如接口和节点维度,可以前往接口详情或者节点详情,后续会提供对应的跳转能力。
配置项 | 描述 |
开启状态 |
|
总QPS阈值 | 节点维度总QPS阈值。 |
例外设置 | 详见例外项。 |
总并发限流
简介
总并发限流将以节点维度统计总并发(单节点所有服务端接口并发之和),对于超过阈值的请求进行限流操作。
生效范围
总并发限流对所有服务端接口生效,优先级低于流量防护规则。
适用场景
在调用RT较高的场景下(普遍超过1s),QPS限流会出现一个比较明显的问题,当系统的竞争资源(线程池、内存、连接池等)被占用时,会导致排队进一步导致接口的RT上升,如果此时只采用基于QPS的限流,每秒还是会有少量请求进入,而队列中的请求无法在秒级别消化完成,这会导致队列进一步累积,RT进一步上升,无论新旧请求RT都有显著上升。此时如果使用了并发限流,如果还有一定量的请求没有完成,新的请求会被直接拒绝,这样虽然请求会被限流,但是系统处理完当前请求后,新的请求就会通过,以较小的排队时间完成请求。从整体上看,请求的成功率和平均RT都会有显著的改善。
预期外的接口出现突增 -> 竞争资源紧缺 -> 队列堆积 -> 所有请求RT上升。
页面说明
页面左侧为总并发限流事件,右侧展示近5分钟该应用的节点平均总并发请求数据变化趋势,事件为节点+接口维度,基于实际发生总并发限流的节点以及接口上报事件,每5分钟上报一次,上报过去5分钟内发生的限流情况。
单击事件的查看链接,可以查询对应IP节点的总并发,并将时间回放至事件上报时间附近,以观察事件触发时对应节点的总并发以及限流是否符合预期,如果需要查看更加详细的信息,比如接口和节点维度,可以前往接口详情或者节点详情,后续会提供对应的跳转能力。
配置项 | 描述 |
开启状态 |
|
总并发阈值 | 节点维度总并发阈值。 |
例外设置 | 详见例外项。 |
配置流量防护
完成系统防护的配置之后,应用就具备了基本的防护能力。但是在一些场景下仍存在一些风险和问题。
请求模型不同带来的稳定性风险:常见的场景是相同的总QPS下不同读写比例带来系统负载压力是不同的,在一个请求模型下压测得到的结果可能不适用于其他请求模型。
不重要请求占用总QPS配额导致核心接口受损严重:由于总QPS限流不区分接口,如果出现不重要接口的突增会极大占用总QPS配额,导致重要接口出现大量限流。
为了解决以上场景,我们需要配置更细粒度的限流规则,在保障应用稳定性的前提下尽可能减少限流。
对于性能损耗较大的请求,我们根据阈值评估的结果配置接口维度的流量防护规则,MSE 流量防护会先检查接口维度规则再检查系统规则。
对于系统的核心请求,我们根据阈值评估的结果配置接口维度的流量防护规则,并在系统防护中为该接口增加为例外项,系统规则不再检查这些接口,完全由接口维度的流量防护规则来决定是否限流。
所有流量防护规则均有两个配置入口(共享数据),一个是接口详情页面中选中对应接口即可配置,另外一个是流量治理中的流量防护页签。
接口流控
接口流控以QPS阈值作为限流判断标准,具备两种流控效果,分别适用于不同场景。
第一种是快速失败,对于超过阈值的请求直接进行限流操作,适用于大部分场景。
第二种是排队等待,我们会根据配置的阈值来匀速通过请求,比如配置10QPS,同时到达3个请求,那么第一个请求会直接通过,第二个请求会等待100ms后通过,以此类推(如计算出的等待时间大于配置的超时时间会直接拒绝)。
这种流控效果适用于需要削峰填谷的场景,图中,黄色的部分代表超出消息处理能力的部分。把黄色部分的消息平均到之后的空闲时间去处理,这样既可以保证系统负载处在一个稳定的水位,又可以尽可能地处理更多消息。通过配置流控规则,可以达到消息匀速处理的效果。不过需要注意的是这种匀速是基于等待来实现的,会导致一定程度的线程上涨,因此不建议将超时时间配置过长。
并发隔离
并发隔离以并发数阈值作为限流判断标准,对于超出阈值的请求直接进行限流操作。服务端接口的并发隔离主要用于一些慢调用场景,与总并发限流的适用场景相同。
热点参数
热点参数功能主要服务于一些有业务语义的限流需求,可以根据一些参数条件来进行更精细的限流。
热点参数规则根据作用的框架类型不同分为热点参数防护(HTTP)以及热点参数防护(RPC)。
热点参数防护(HTTP)
热点参数防护(HTTP)支持WEB框架和网关框架的服务端资源,在参数选择上自动支持以下位置参数,其中Header以及URL参数需要指定Key值并支持属性值匹配。
Client IP:从 header 中获取,优先级为:X-Forwarded-For(第一位) -> Proxy-Client-IP -> WL-Proxy-Client-IP -> X-Real-IP -> HTTP_CLIENT_IP
Remote Host:从 header 中获取 Host
Header
URL参数:指的是URL末尾的请求参数(查询参数)
在不选择属性值匹配时,会为每个参数的值分别统计限流;选择属性值匹配时,规则只对匹配上的参数的值分别统计限流。
阈值支持多种时间维度,流控方式和接口流控一样支持快速失败和匀速排队。
热点参数防护(RPC)
热点参数防护(RPC)支持RPC框架的服务端资源以及自定义接口,需要指定参数位置索引,会为该位置参数的值分别统计限流。统计周期时间支持任意秒。
增加例外项
前面提到对于核心请求,处理额外配置接口粒度的流量防护规则之外,我们还需要将他们增加进例外项中,来保证他们不会被系统防护限制。所有的系统防护功能都支持例外项的配置,在对应功能的设置界面可以添加例外项。
左侧为可供直接选择的接口(近期有调用的接口),对于未展示在左侧的接口可以直接在输入框处输入后回车添加到已选接口中。
例外项会使该资源不再被系统规则限流,但是仍会参与总QPS/并发统计。
优化限流行为
完成了前面几个步骤之后,应用的在入口流量不确定这个场景的下稳定性得以保障,但是默认的429返回在一些场景下对业务是不友好的,我们可以通过自定义行为来优化限流后的返回值,优化业务表现。各个框架的自定义行为支持如下表所示。
WEB自定义返回 | RPC自定义返回 | RPC自定义异常 | |
SpringMVC | ✅ | ❌ | ❌ |
Zuul | ✅ | ❌ | ❌ |
SpringCloud Gateway | ✅ | ❌ | ❌ |
Dubbo | ❌ | ✅ | ✅ |
自定义埋点 | 自定义埋点需要在代码层面 catch 异常,详见自定义埋点。 |
不稳定的出口流量
阈值评估
对于不稳定的出口流量的阈值评估主要集中于RT以及异常比例,可以根据历史的数据结合业务需求来确定,这里不再赘述。
配置系统防护(客户端防护)
客户端防护的主要手段是熔断,通过熔断我们可以在依赖的服务出现问题时,仍能提供一定程度的正常返回,保障应用不被完全拖垮,避免出现非核心服务故障影响核心应用稳定性的问题。
在系统防护(客户端防护)中我们可以根据应用的特征来选择对应的功能,不建议在客户端防护中同时配置两种熔断。
出口流量RT较为稳定,应用对RT较为敏感 -> 慢调用熔断
其他场景 -> 异常调用熔断
慢调用熔断
简介
慢调用熔断会分别统计每个客户端接口的慢调用比例,当慢调用比例大于配置的阈值时对该接口进行熔断,熔断期间该接口直接快速失败,并相隔一定时间通过用于探测的请求,探测成功后结束熔断。
生效范围
慢调用熔断对所有客户端接口生效,但对于配置了接口维度熔断规则的接口不生效。
适用场景
慢调用熔断的使用场景基本和异常调用熔断中的超时异常相同,区别在于可以动态调整判断慢调用的RT标准,不依赖超时配置。
页面说明
页面左侧为慢调用熔断事件,右侧展示近5分钟该应用的TOP10平均RT,事件为节点和接口维度,基于实际发生异常调用熔断的节点以及接口上报事件,每5分钟上报一次,上报过去5分钟内发生的限流情况。
配置项 | 描述 |
开启状态 |
|
慢调用RT (ms) | 超过该值的请求会被判定为慢调用。 |
降级阈值 (%) | 当有超过该百分比阈值的请求RT长于上述慢调用RT,则触发熔断。 |
例外设置 | 详见例外项。 |
高级配置 | |
统计窗口时长(秒) | 统计的时间窗口长度,取值范围为1秒~120分钟。 |
熔断时长(s) | 即熔断触发后持续的时间。资源进入熔断状态后,在配置的熔断时长内,请求都会快速失败。 |
最小请求数目 | 触发熔断的最小请求数目,若当前统计窗口内的请求数小于此值,即使达到熔断条件规则也不会触发。 |
熔断恢复策略 | 熔断器进入恢复阶段(半开启状态)的恢复策略。
|
异常调用熔断
简介
异常调用熔断会分别统计每个客户端接口的异常比例,当异常比例大于配置的阈值时对该接口进行熔断,熔断期间该接口直接快速失败,并相隔一定时间通过用于探测的请求,探测成功后结束熔断。
生效范围
异常调用熔断对所有客户端接口生效,但对于配置了接口维度熔断规则的接口不生效。
适用场景
异常调用熔断主要分为两类场景。
超时场景:一个客户端接口的超时异常比例过高,往往是服务提供方出现异常情况,但这会进一步导致调用方(本应用)请求堆积,影响本应用的其他接口,通过熔断的方式可以在提供方异常期间快速失败,避免堆积。
非超时场景:一个客户端接口的非超时异常比例过高,异常调用熔断能够通过抛出限流异常供用户处理,起到降级的效果,优化异常场景下的用户体验。
页面说明
页面左侧为异常调用熔断事件,右侧展示近5分钟该应用的TOP10异常比例接口,事件为节点+接口维度,基于实际发生异常调用熔断的节点以及接口上报事件,每5分钟上报一次,上报过去5分钟内发生的限流。
配置项 | 描述 |
开启状态 |
|
熔断比例阈值(%) | 接口维度熔断比例阈值。 |
例外设置 | 详见例外项。 |
高级配置 | |
统计窗口时长(秒) | 统计的时间窗口长度,取值范围为1秒~120分钟。 |
熔断时长(s) | 即熔断触发后持续的时间。资源进入熔断状态后,在配置的熔断时长内,请求都会快速失败。 |
最小请求数目 | 触发熔断的最小请求数目,若当前统计窗口内的请求数小于此值,即使达到熔断条件规则也不会触发。 |
熔断恢复策略 | 熔断器进入恢复阶段(半开启状态)的恢复策略。
|
配置流量防护
完成系统防护的配置之后,应用就具备了基本的防护能力。但是接口对于慢调用以及异常比例的容忍程度,系统防护这种批量行为可能不满足部分接口的防护需求。为此我们可以为这些接口在流量防护页签中配置接口维度的熔断规则,系统防护中客户端防护会自动将这些配置了接口维度熔断规则的资源作为例外项处理,所以不需要手动增加例外项配置。
熔断规则中的各项配置与系统防护(客户端防护)中均一致,只是生效范围为指定接口,此处不再赘述。
优化限流行为
完成了前面几个步骤之后,应用的在出口流量不稳定这个场景下的稳定性得以保障,但是默认的429返回在一些场景下对业务是不友好的,我们可以通过自定义行为来优化限流后的返回值,提升业务表现。各个框架的自定义行为支持如下表所示。
WEB自定义返回 | RPC自定义返回 | RPC自定义异常 | |
Dubbo | ❌ | ✅ | ✅ |
Feign | Feign 框架兼容自身 fallback 机制,不支持自定义行为 | ||
自定义埋点 | 自定义埋点需要在代码层面 catch 异常,详见自定义埋点。 |
其他能力
自定义埋点
对于 MSE Agent 没有支持的框架资源定义,用户可以参考文档增加自定义接口,详情可参见如何在微服务治理中添加自定义接口。
可观测
控制台可观测
指标
MSE 控制台提供近5分钟的各种指标查询以及展示能力,并支持1天内回放能力,指标解释如下。
指标名称 | 指标类型 | 说明 |
总QPS | 区间值 | 入口QPS = 通过QPS + 拒绝QPS。 |
通过QPS | 区间值 | 未触发流量防护规则,通过的流量(此时调用还未开始)。 |
拒绝QPS | 区间值 | 被流量防护规则触发拒绝的流量。 |
异常QPS | 区间值 | 出现Java异常的数目。 |
平均响应时间 | 区间值 | 平均RT,在请求完成态进行统计。 |
并发 | 瞬时值 | 调用正在进行的数目,暂时这一秒中最大并发值。 |
事件
MSE 流量防护事件可在 MSE 控制台各级事件中心中查询,资源类型统一为应用,事件类型统一为高可用通知。自适应过载保护事件实时上报,其余流量防护事件每5分钟上报一次。
可观测扩展
常见问题
没有到阈值就发生限流
首先可能是流量不均导致部分节点到达阈值:MSE 侧限流均为单机限流,所以当流量有不均情况的话,可能部分节点到达阈值发生限流,部分节点没有到达阈值。假设我们配置了/a接口阈值10QPS,共有3个节点,其中一个节点到达QPS15,通过QPS10,拒绝QPS5,另外两个节点到达QPS和通过QPS均为5,此时在接口详情看到/a接口通过QPS20,拒绝QPS5,通过QPS小于30(3*10)QPS,这是符合预期,用户可以考虑解决流量不均的问题。另外,用户可以在接口详情页面选中有疑问的接口后,在右侧页面选中节点详情查看该接口各个节点的指标,以确认该问题。
如果确实单节点上出现了没有达到阈值就发生限流的情况,很有可能是由于 Sentinel 为了尽可能保证任意相对秒内不超过阈值而做的设计导致的,符合预期。假设以下这个场景,请求按照图示的时间顺序到达应用(每个箭头表示10个请求),限流阈值配置为50QPS。如果我们基于最基本的令牌桶算法或者计数限流法,由于第0秒到第1秒共40个请求,第1秒到第2秒共40个请求,均小于阈值,所以都会通过请求。但是在第0.5秒到第1.5秒的这1秒内会实际通过70个请求,远大于阈值,可能会对系统的稳定性带来风险。
为了避免这种场景下带来的稳定性风险,Sentinel 采用了滑动窗口算法,在遇到类似于上诉场景时,第0秒到第1秒的40个请求都会被通过,第1秒到第1.5秒的40个请求只有前20个会被通过,这样第0.5秒到第1.5秒也同样只有50个请求通过,保障了该场景下系统的稳定性。但是由于指标统计和上报是秒级别的,所以从指标看第0秒到第1秒通过QPS为40,第1秒到第二秒通过QPS为20,拒绝QPS为20,看起来没有到阈值就发生了限流,但实际是符合预期,且有利于系统的稳定性。
实际通过QPS大于阈值
Sentinel 在整体设计中关注性能表现,因此在一些场景下会出现实际通过QPS大于阈值的情况,幅度基本控制在1QPS,对准确性有较高需求的用户建议通过压测进行场景验证。