流量控制在单体应用的时候也同样存在,在微服务化之后需要进行流控的场景进一步增加了。

典型的微服务架构如下,网关接收请求,路由RPC到后端应用,应用之间直接通过RPC互相调用,限流可以应用的地方就是在各入口处,例如网关接收到的HTTP请求,服务提供的RPC调用。

Flow Control

策略

限流策略有不少,典型的有,

  • 单机QPS限流。统计单进程内的QPS,在进程内进行运算,无需引入外部依赖。
  • 全局QPS限流。统计相同接口在集群多台机器上的全局QPS统计,需要引入外部依赖,如Redis,进行QPS计数统计。
  • 全局高频限流。计算模型同全局QPS,但关注接口关联的参数,例如一个用户单位时间内访问一定次数,或是特定资源参数单位时间内访问一定次数。

问题

单机QPS限流

易失效或过敏感

随着应用集群规模的增长,单机QPS适用性会逐渐降低。机器规模增长,则单机分配到的请求必然降低。单机QPS阈值设置大了可能没效果,设小了又过于敏感。如果调度策略略微不均或是部分节点故障被剔除之后,均可能造成一些设置了低阈值的单机限流被触发。

全局QPS限流

外部依赖降低了稳定性

全局QPS在触发上不会那么敏感,但是为了计算全局数据,则必然需要引入外部依赖。常见的就是引入类似Redis一样的KV存储去维护滑动窗口内的QPS统计。外部依赖的增加无疑降低了服务的可用性。在依赖服务异常时可能会造成限流功能不可用或是触发更为严重的问题。限流场景下的计算天然就是大流量的,对依赖服务的压力显而易见,因此这并不是一个可以忽略或是低概率发生的问题。

统计精度与性能压力的权衡

如果每来一个请求都很精确的进行全局计算,那么外部依赖的压力会很高。如果进行一定的预取以及本地计算,则容易造成统计不准确。这一点在集群规模很大的情况下易出现。

限流阈值的维护性问题

限流的目标是什么

个人理解是在系统能够提供的最大允许范围内尽可能提供服务,拒绝掉系统承载能力之外的请求以保证服务稳定性。

按照这个目标,那么限流阈值配置成多少就不是一个易于回答的问题。应用的承载能力在代码变更或是部署调整时都可能产生较大的变化。这类变化往往不能在事前被维护人员意识到,或者是单纯遗忘了存在着的限流配置,更常见的是初始设置成什么样的阈值也难以去度量。

如何获得合理的QPS阈值

一种思路是通过压测去对系统进行真实的度量,最后将结果关联到降级配置。这种方式的问题在于压测模型与线上真是运行环境不一定相同,但接口的压测不能说明问题,混合接口压测又难以真实反应实际流量场景。

另一种思路是通过梳理各应用监控数据,从当前系统高峰期的QPS统计,通过预定义的计算公式来计算预期的限流QPS值。通常是根据高峰期的水位情况进行一定的放大。这种方式的问题在于系统性能拐点未知,单纯的预测不一定准确。

是否可以基于系统运行反馈自动设置QPS阈值

单纯的自动设置,是很容易的。例如可以采集每一个API在每日高峰期全集群的QPS数值,乘以一定系数进行设置。但是这个值的合理性很难保证,系数可能保守可能乐观,同样会导致限流策略的无效。

如果先按照上述方式获得了一个限流值,随后根据系统运行情况去调整会如何呢?假定初始QPS阈值设高了,在限流未触发时,应用集群负载出现了问题。此时通过监控巡检去发现这一问题,反向调低QPS阈值。反过来当初始阈值低了,那么在限流触发情况下,应用负载还是正常,那莪这个时候去自动调大阈值。

看上去可以,但去实施时会发现难以动手。应用可能对外提供多个接口,可能只部分接口有限流配置。即便观测到了负载与预期不符,但与预期的差异可能是别的原因造成的,也可能是未配置限流接口带来的。此时调整已有接口的阈值就都是无用的。

思考

思考上面的问题,准确的阈值是无法通过非实时方式获取到的,如果想实时获取,那么必然要基于应用的运行状况反馈。单机器的反馈是最准确的。因此更合理的限流方式可能需要回归到单机上。

应用可能提供多个接口,任何一个接口都可能是问题所在导致负载问题。从应用的运行数据反馈中也难以准确的获取究竟是哪一个接口造成的影响。可能是单个异常接口,也可能是每个接口都贡献了一份。也就是说,更有效的限流方式是应用级的,因此不是基于QPS去进行限流,而是基于应用的负载状况去进行限流。

微信的这篇论文Overload Control for Scaling WeChat Microservices描述的就是应用级别的负载保护策略。论文中应用通过RPC队列平均等待时间这一指标去判定应用是否处在过载状态,如果过载则按请求优先级逐步限流,如果正常则逐步恢复。如果微信的系统真实采用了论文中的方案,那么自然这个策略就肯定有效。单纯进行分析,也不难发现上述策略的合理性。

实际应用中可能是多种限流策略的组合。对于特定请求进行高频限流保护,对于可以稳定梳理维护的接口通过全局QPS保护,之后再通过上述应用级限流策略进行整体兜底。多方组合,才有助于提升系统稳定性。