使用 MSE 流量防护轻松面对运行态流量不确定风险的最佳实践

作者:启淮、屿山

一、流量变化引发的风险不容小觑

如今各个行业发展都十分迅猛,系统架构的设计越来越复杂,系统所承接的流量经常是变化多样、并且难以预测。比如前不久刚过去的 618 购物狂欢节,电商平台的流量节节攀升、翻倍增加,成千上万的用户在同一时间涌入平台,进行抢购、下单、支付等操作,这对系统的承载能力提出了极大的挑战。

如果没有为应用做好充足的准备,很可能会面临响应延迟和错误率飙升的问题,严重可能会出现系统夯死的情况。轻则对用户的体验大打折扣,重则可能导致出现资金损失、品牌声誉受损,甚至引发法律纠纷。流量变化带来的稳定性风险,现在已经成为系统架构师、稳定性团队不得不面对的难题与挑战。

问题的根源是什么?

这些问题究竟是怎么产生的呢?我们从单个应用的视角来分析,流量可以分为上游调用我方系统的流量(服务端)和我方系统调用下游应用的流量(客户端)。两类流量的变化都会对应用提供的服务带来稳定性风险。

根源 1:收到的流量是不确定的

代表场景有电商促销、节日重大活动、热点事件等。这些流量是不确定的,在一些场景下,流量可能会翻数倍,远远超过应用所能承受的容量上限,这就会导致所有请求RT暴涨、系统宕机,影响服务的整体可用性。

根源 2:调出的流量是不确定的

代表场景有系统依赖了第三方算法、支付服务等。第三方系统的稳定性是我们无法控制的,可能在某一时刻,第三方系统出现突发的大量异常或者慢调用。若没有妥善处理好这些第三方依赖服务的异常或慢调用,系统的业务可能会跟着一起出错,或者发生一些预期外的现象。而且这种单服务的异常在未正确处理的情况下,影响面还会逐步扩散到整个系统,导致整条业务链路都不可用。

二、如何解决流量不确定带来的风险

在我们实际部署和管理系统时,最常见的流量不确定导致的问题,一共可以总结出 4 种类型的场景:

  1. 收到的流量整体增加,所有接口的请求数都在变多
  2. 收到的流量整体变化不大,但是对于部分接口,具备某些特征的流量不正常地飙升
  3. 依赖的对端服务大量抛异常
  4. 依赖的对端服务大量存在慢调用/超时,进而拖垮当前系统

接下来我们会通过 Demo 对这 4 个场景进行模拟,为大家分享如何使用阿里云微服务引擎 MSE 服务治理的流量防护能力来解决这些问题。

场景 1:活动/大促突发导致的流量整体增高

第一个场景,因为大促、活动、时事热点等情形给系统带来成倍的骤增流量,系统在未进行任何准备的情况下无法承接如此高的流量,CPU 使用率很快会被打满,系统整体的 RT/错误率也随之上涨,可用性大幅降低。我们可以在 MSE 应用概览中,观测到这些情况:

在服务消费端可以看到,该服务 RT 大幅升高,系统开始出现许多报错:

在这种场景下,我们自然会想到限流这一能力,通过将超过系统容量的请求进行拒绝,从而恢复系统的处理能力。MSE 提供自适应过载保护的能力,通过拒绝掉一部分流量,让系统 CPU 占用率尽可能维持在配置的预期水位附近,为系统树立一道坚强的防线。我们可以看到随着自适应过载保护的开启,超出系统处理能力的请求被拒绝,系统整体的 RT 和 CPU 也趋于平稳的监控水位,没有被大流量击垮:

在服务消费端也可以看到,开启自适应过载保护的服务,其 RT 时间也大幅降低,错误请求数也降低至 0:

在上述的例子中,我们没有为任何接口配置精确的限流阈值,而是通过开启 MSE 自适应过载保护的能力,保障系统 CPU 水位处于一个安全的水平,让系统整体处于一个可用的状态。

不过这种配置方式是无差别地对每个接口进行了限制,在系统 CPU 压力较大的情况下,核心接口和边缘接口一视同仁都被限流。而我们更期望的是,更多限制边缘接口,让核心接口尽可能对外提供服务,因此最佳的实践是,我们应该提前为每个核心接口行多次压测,得出一个合理的限流阈值后,专门为这些接口配置好独立的、接口级别的限流规则,并在自适应过载保护中配置例外项,在系统整体限流时忽略这些接口,采用这些接口专门配置的规则的限流阈值来进行限流。

对于那些非核心的接口,我们就可以用自适应过载保护为他们整体提供一道兜底地、无差别的防御能力。

场景 2:热点流量突然飙高

很多时候我们系统整体上大部分接口的流量可能没有太大变化,但是某个或某几类接口的调用量却发生了激增。比如可能是某个用户异常频繁的请求或是对某个资源集中频繁的访问。同样的,我们通过 Demo 来模拟这个场景,/product/order 接口在某个时间访问量激增,通过业务观测发现是某些特定用户进行频繁的刷单。可以看到这些刷单请求将系统整体的并发数打到了 200,将 tomcat 业务线程池打满。

此时我们可以看到不仅线程数被打满,系统整体 CPU 使用率也发生大幅飙高:

此时系统中其他接口例如 /product/check,在系统线程数耗尽、CPU 被打满的情形下,请求无法被执行,进而出现 qps 大幅下跌的情况,并且接口响应时间也大幅升高:

而此时我们开启自适应过载保护,或者为 /product/order 配置精确的接口限流都无法解决这个刷单的场景,因为即便我们通过开启这这两种限流能力,将 /product/order 的 qps 降低到原来的一半,但是在刷单客户占用大量请求比例的情况下,这两种限流只会让普通客户的请求得到执行的概率更低。

这时候我们就需要使用 MSE 提供的热点参数防护的能力,通过配置规则,我们可以根据请求中的 header、客户端 IP、URL 参数等信息进行更细粒度的限流,从而做到对有这些特征的请求进行限流,不影响其他正常请求。在该 Demo 场景中,我们对 userId 参数专门进行限流,当规则生效之后,可以看到异常的请求被限流:

系统资源开销开始恢复,其他接口的调用也恢复正常:


此时我们分别模拟正常和异常的请求去发起调用,发现普通用户可以直接访问:

异常用户(uid=12356668)的请求在达到一定频次后访问就被限流(模拟数据):

场景 3:依赖的服务提供方大量出现异常

在实际的生产实践中,我们常常会遇到一种情况,我们应用本身的流量没有太大的变化,但是由于应用依赖的服务质量出现问题,往往是 RT 变慢或者异常比例上升,导致应用自身的稳定性出现问题。

我们准备了一个 Demo 来演示这个场景,我们构造了一个订单风控审查请求(/product/risk-check),在应用收到请求后,会调用两个依赖的服务,一个是相对核心的服务(订单风险校验 /risck/check),一个是相对边缘的服务(订单快照生成 /order/generateOrderScreenshot)。现在我们对后者这个边缘服务注入故障,可以看到应用自身请求的异常比例开始上升。订单风控审查业务出现异常后,可能会导致线上大额对公业务订单无法及时生成,影响营收,严重的话可能会影响客户正常生产运营,甚至会带来声誉损害和负面的社会影响。

在应用的调用端可以明显感受到风控审查这一核心业务能力有损,我们发送少量调试请求可以发现,目前接口确实有较大概率出现 5xx 报错:

此时可以使用 MSE 服务熔断的功能来解决这个场景,我们可以通过一键开启系统防护中异常比例的默认熔断,熔断掉异常的服务,返回降级的数据。

可以看到随着规则的开启,边缘服务被熔断,应用接口的异常比例下降,从用户侧的表现看,请求能够正常返回,只是缺少了边缘服务提供的一些相对不重要的信息。

和前面的场景一样,在默认熔断的基础上,我们可以给特定的接口配置单独的熔断规则,他们会自动被添加到例外项中,这边就不再赘述。

场景 4:慢调用拖垮整个系统

慢调用是生产实践中常见的问题,它可能是因为系统本身接口有极大的 IO 开销或者长时间资源等待的情况,也有可能是需要调用的关联系统的接口,会有非常长的 RT 耗时。无论是何种情况,随着请求数增多,它们都会导致应用业务线程池中的线程被持续占用无法释放,进一步导致其他正常请求获取不到线程,无法处理。特定接口出现慢调用后,系统中其他原来正常的请求也会因为排队等待业务线程,出现 RT 开始上升的情况。

我们也可以通过 Demo 来演示这个场景。现在模拟风控检查接口 /product/risk-check 出现慢调用的场景,每次检查会花费大量时间,虽然偶尔一两笔慢请求不会对系统产生什么影响,但是当请求逐渐增多时,慢调用的弊端逐渐显现:其他正常的接口比如下订单 /product/order,由于抢占不到线程,开始出现明显的 rt 增加,qps 下降的情况:

在服务调用方,会发现由于服务提供方被慢调用拖垮,调用方的请求无法打入,产生大量报错:

此时我们再在外部调用商品快照信息 /product/product-snapshot 接口来测试系统可用性,可以发现请求全部出错,系统已经无法正常响应请求:

这种情况我们可以通过 MSE 并发隔离来处理这个场景,对这类慢请求进行限制,例如并发隔离阈值为 5 时,在已经有 5 个请求正在处理时,新到达的请求会被拒绝,直到有请求完成处理,空出新的线程来处理后续到来的请求。

在 Demo 中,我们通过 MSE 并发隔离功能,将 /product/risk-check 的并发数限制在 10,这样慢调用的请求只会占用系统最多 10 个线程,让系统其他请求可以正常调用,qps 数据开始恢复正常:

调用方的错误率和 RT 也开始大幅下降:

此时再在外部测试系统可用性,发现系统可以正常响应请求了:

三、MSE 流量防护最佳实践是什么

从前面的 Demo 演示中我们可以看到借助 MSE 的流量防护能力,能够解决大部分由于流量不确定和不稳定带来的运行时稳定性风险。为了更直观的演示效果,在 Demo 中我们都是先出现问题再用对应的能力来解决,但是在实际的生产实践中,我们推荐的流量防护最佳实践是:事前评估配置,事中观察调整,事后回溯优化。

首先,我们需要为系统配置好系统防护,这是在宏观层面,考虑一个应用可能承受的资源压力,能抗住的流量阈值来设置的,我们建议配置好基于峰值数据的 1.2 到 1.5 倍来设置这些限流阈值。在配置时分为以下步骤:

  1. 根据单机的资源规格,来配置 CPU 自适应过载保护阈值,推荐 60% 到 70%
  2. 根据容量测试结果,为系统设置总 qps 限流
  3. 根据系统按并发压测数据以及应用的线程池配置,为系统配置总并发限流
  4. 根据历史监控数据为客户端配置异常比例或超时时间的默认熔断

接着,我们需要为系统的核心接口配置单独的限流规则。在这个过程中,我们需要单独压测每条核心业务链路,为核心接口做好容量阈值评估,并且参考压测结果为单个接口配置好限流、隔离规则;根据慢调用比例、压测出错情况,为依赖服务配置好熔断规则。注意这一步完成后,还需要将配置过独立的限流规则的接口在 MSE 系统防护中添加一下例外项。

此外,我们还应该考虑到,每个接口发生限流时,是直接返回 429 异常,还是应该返回一个自定义的,友好的报错提示?这需要我们从业务视角来评估,上游系统是谁,当这个接口因为负载过高而不可用时,我们应该对其返回怎样的内容。

在系统真正上线、运行起来之后,我们需要观察并记录系统运行态实际的资源水位,各类业务接口的出错情况、RT 响应时间表现、历史监控数据来调整我们的流量防护规则的阈值和行为。通过历史数据回溯 + 经验总结 + 行为优化的方式,不断调整、更新应用的流量防护规则,这样才算为一个应用真正做好了流量防护。

🔥🔥拥抱 AI 原生!

8月29日深圳,企业实践工作坊火热报名中!

阿里云诚挚邀请您参加【AI 原生,智构未来------AI 原生架构与企业实践】工作坊,从开发范式到工程化实践,全链路解析AI原生架构奥秘,与AI先行者共探增长新机遇。

⬇️ 点击此处,立即了解完整议程!

相关推荐
小二·14 小时前
Docker+K8s生产级部署实战:从0到1打造高可用微服务集群
docker·微服务·kubernetes
fanly114 天前
Surging AI Agent 完整产品介绍
微服务·microservice
蝎子莱莱爱打怪11 天前
XZLL-IM干货系列 04|Netty 长连接实战:Pipeline 怎么排、心跳怎么跳、连接怎么管
后端·微服务·面试
SamDeepThinking12 天前
Java微服务练习方式
java·后端·微服务
米丘15 天前
微前端之 Web Components 完全指南
微服务·html
霸道流氓气质17 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
霸道流氓气质18 天前
Spring Boot 微服务性能优化完全指南
spring boot·微服务·性能优化
地瓜伯伯18 天前
从MESI缓存一致性协议讲透synchronized的底层
java·spring boot·spring·spring cloud·微服务·springcloud
Devin~Y18 天前
大厂 Java 面试实录:从音视频内容社区到 AI RAG 的全链路技术设计
java·spring boot·redis·spring cloud·微服务·kafka·音视频
递归尽头是星辰18 天前
AI 访问数据仓库:从直连到微服务化
数据仓库·人工智能·微服务·dataagent·ai数据治理