Categories
程式開發

当流量突变时,系统中的负载保护如何应对?


有人说:“击垮一家互联网公司的最快方式不是运营疯狂烧钱、不是产品经理胡乱决策,而是系统核心功能的持续不可用。”的确,我们常会见到一次大规模宕机导致企业损失上千万,甚至市值大量蒸发。由此可见,系统稳定性和高可用的重要性。

在企业业务系统中,技术选型多种多样,系统的性能和健壮性参差不齐,业务流量变化无常,如何才能保证各种业务流量安全运营,系统在任何情况下都不会崩溃呢?InfoQ 采访了 QCon 全球软件开发大会(北京站)2020 的讲师朱龙云,他和我们分享了关于系统负载保护的设计原则,以及发生流量突变时的具体应对措施。

负载保护的总体原则

虽然业务系统在正常情况通常是感知不到负载保护的存在,但是负载保护却是所有系统都必须具备的能力,它可以防止系统“崩溃”。那么,常见系统的负载保护在设计之初一般都遵循什么样的设计原则呢?

朱龙云表示:虽然各个系统的负载保护设计都会有自身的特点,但从设计角度从下到上基本可分四层:

  • 通用层:这一层是系统通常都需要的基础属性。例如:易部署、高度可观察,具备容灾和扩缩容能力,隔离失败 (服务 / 模块)
  • 机制层:这一层包含了与负载保护密切相关的基础属性,在负载保护系统演化中相对策略层变化是慢的。例如低消耗、低耦合,容错性、健壮性、适应性、伸缩性和异构性。
  • 策略层:这一层是负载保护的策略算法,是在系统外侧最容易被看到的,容易引起广泛的讨论。我们常讲到的限流、降级和熔断算法就在这一层。
  • 系统层:这一层主要是根据负载保护运营反馈,找出系统整体设计的缺陷和不足,并进行相应调整,并将系统设计与自动扩容融合。

如果具体到负载策略的设计原则,一般需要考虑到调用链、业务与资源、整体与局部、扩展性等等。

  • 调用链:在这一维度,我们需要管控好上游入口的流量,实时计算自身服务资源消耗,并实时评估下游服务服务质量,确保整个调用链整体流量安全。
  • 业务与资源:通常,业务流量会有时间错峰,我们可以将错峰业务部署在一起,提高机器资源利用率。但这样可能会导致业务之间相互影响,负载保护就在此时发挥作用,当业务负载超过自身预分配资源时,继续使用系统尚未被其他业务消耗的资源。当各个业务总体消耗资源超过系统整个负载安全上限,按一定策略对相应业务进行限制;
  • 整体与局部:达到整体效果的负载保护需要“均匀”分配到各个最小计算单元,但这里的“均匀”不是绝对数量的“均匀”,而是与每个计算单元相匹配的“均匀”,即与系统的负载均衡相匹配;
  • 扩展性:业务对负载保护会有扩展性的需求,例如按照业务重要程度、用户分布或者资源消耗等进行个性化限流以及降级服务。

整体系统和单个服务的负载如何平衡?

系统的负载保护既包括单个服务的负载保护,也包括整体系统的负载保护,前文我们也讲到了整体与局部负载保护的设计原则的关键词是“均匀”,如何实现“均匀”呢?

我们可以分两个部分来讨论:

第一部分是整体系统与调用 topo 中某个节点关系。我们需要先了解系统中的负载脆弱点以及原因。

负载脆弱点可能是技术引起的,例如动态语言实现服务,性能就会差一些;可能是资源引起的,例如部署的物理资源较少;可能是业务引起的,例如计算密集或者外部依赖的问题。然后,我们需要针对脆弱点做好流量管理,自身负载的实时监测,对脆弱点上游做好熔断机制,并做好监测预警,防止该点引起系统整体雪崩。同时从长远角度考虑,还要尽可能优化或者消除因设计引起的系统脆弱点。

第二部分是单点服务集群与计算单元的关系。

集群中各个计算单元需要负载均衡,而通常情况下,各个计算单元是异构的,这就需要匹配相应的计算资源能力。负载保护需要保证当系统过载时,负载依旧是均衡的,避免出现有的计算单元过载,有的计算单元还有资源剩余。

总之,负载保护的策略具体实现都需落实到单个计算资源,同时各个计算资源的信息需要上报汇总起来做整体决策。

上游模块与下游服务的负载保护

除了整体系统与单个服务,我们还常会遇到上游模块与下游服务之间的负载保护。朱龙云表示:“上下游之间的负载保护最重要的是做到相互信任不依赖,对方通知异常,能根据通知信息采取措施。对方不通知也能及时发现异常,采取措施,不会因为自身原因造成系统雪崩。”

展开来讲就是上游需要为下游做好流量保护,但是下游不依赖这种保护。当上游没有保护好,系统流量过载,下游能触发自身的负载保护,当上游故障,流量突然下降,下游也能发现。如果下游服务异常,不能提供正常服务,下游需主动通知上游。如果没有通知,上游也需要能自身发现异常,并采取措施。而当下游服务正常时,上游也要及时探测到,并开放对外服务。

如何评判负载保护的效果

一个系统的负载保护设计好了之后,如何评估设计得是否合理呢?一般来说,我们会通过以下指标来判断:

  • 低能耗:负载保护本身不能挤占太多的系统资源;
  • 适应性:负载保护本身对流量变化不敏感,不会因为业务流量变化而频繁扩缩容;
  • 容灾能力:负载保护的作用是帮助业务尽快恢复,所以自己不能成为瓶颈,自身故障不能影响到整个业务系统;
  • 透明扩缩容:业务系统扩缩容时,应该感觉不到负载保护的存在;
  • 健壮性:保护机制触发时机既不能过于敏感,造成系统颠簸,同时也不能迟钝。

一个真实存在的系统往往都是有自身服务的职责和边界,因此一个合格的负载保护系统也要协助系统在各种场景下对外提供服务。

  1. 在一定负载范围内,系统在不触发负载保护的情况下,对外提供高可用服务;
  2. 系统对外提供有损服务,这种场景已经触发负载保护;
  3. 系统只保证自己正常运行,不再保证对外服务的数量,排除异常之后,系统可以立刻对外提供高可用服务,这种场景需要负载保护深度接入;
  4. 系统不对外服务,异常排除之后,结合线下处理,系统在可预测、可控时间范围内恢复对外服务。

流量突变时,负载保护如何应对?

当流量发生突变,系统中的负载保护应该采取什么样的措施呢?

负载过高

当系统负载过高时,通常会采用三种措施来保护系统,限流、降级和熔断。

限流一般是指限制入口流量,触发机制可以是提前预设阈值、下游通知或者自发检测到资源过载,而具体策略可以是刚性,也可以是弹性。一般来说,刚性是指超过一定量之后,就全部不再提供服务,而弹性就会涉及降级。

降级一般是指限制业务或用户。如果是读写分离的业务,通常读的量比较大,写的量比较小,而且敏感,所以当系统过载时,就会限制读的量,而写的量不会直接限制。读少了,同时会自动引起写减少。这种方式即使是触发了负载保护,也会使系统尽可能有好的体验。限制用户指的是按照一定策略使得部分用户无法访问该服务。

熔断主要是发现自身或者下游不能正常服务,主动不再对上游提供服务。但是熔断也可以引入降级服务策略,允许部分服务正常运行。

总体来看,限流偏向于管理入口流量,熔断偏向于保护下游服务。降级是为了在系统触发负载保护时,尽可能提供弹性服务。一般来说,熔断比限流严重,而限流比降级严重。

负载过低

与负载过高相比,大家对于负载过低的关注度会比较低,但其实负载过低有时也会带来严重后果。

一般来说,出现负载过低的情况有以下几种:

第一种是比较直接的,业务量太少,这种情况的解决方法就是与其它业务混合部署,再采用负载保护;

第二种是关键业务或节点进行了独立部分,这种情况一般是企业根据业务情况做出的运营策略,不需要调整;

第三种是服务实现的缺陷,例如服务里面存在 idle 操作,造成机器资源利用率不高;

第四种是服务设计的缺陷,例如 QPS 达到一定阈值,服务器性能就会断崖式下降,造成机器资源利用率不高。

负载均衡

负载过高或过低都不可行,我们需要做到负载均衡。首先,调用链节点之间的负载要均衡,没有明显瓶颈,否则容易触发负载保护;其次,单个服务集群的各个服务器之间的负载要均衡,包括硬件和软件的负载均衡。

如何实现负载均衡呢?朱龙云认为主要考虑以下几个方面:

  1. 系统的冷启动策略,可以从一个预设的平衡条件开始逐步调整;
  2. 系统性能指标,例如响应时间,错误码等;
  3. 负载调度策略,基于机器的服务能力来分配,例如服务的平均响应时间、成功比例以及内部队列长度等;
  4. 负载均衡的精度和稳定性,精度高的好处是能够区分出异构机器的服务能力,而坏处在于频繁调整各个机器服务权重,有可能引起系统颠簸;
  5. 负载均衡接入方式,常见的有网关接入和 Agent 接入;
  6. 即使触发了负载保护依然需要负载均衡,不然就有可能造成系统服务能力浪费。

企业中的负载保护存在哪些问题?

在企业中,我们经常会遇到这三种负载保护的情况:第一种是企业在系统设计之初就考虑负载保护,系统上线之后没有出现大故障,负载保护不断优化、完善,并与业务逻辑进一步融合;第二种是在系统运营过程中遇到了负载保护的问题,并得到了比较好的解决;第三种是遇到了负载保护问题,不能彻底解决,负载保护系统部分可用,需要堆人力来运营系统。

第一种情况是最理想的,但是现实中很少见,因为这其中不只涉及到技术问题,同时也需要考虑项目进度、成本等多种因素。而其它两种情况都是企业实践中比较常见,当系统平稳运营时,资源利用率的情况也比较好,一旦发生外部流量陡增或者是关键路径触发异常等情况,各个服务之间的连锁反应可能会使整个系统崩溃,短时间内无法恢复,同时共享资源的业务也会受到影响。

在单体架构阶段,负载保护会比较简单,系统崩溃了也较容易恢复。而在微服务阶场景下,负载保护就复杂了,由于调用关系复杂,系统因过载崩溃了,需要先找到引起奔溃的节点,有时可能是几个节点连环触发的,系统无法通过简单扩容恢复。

所以,我们需要在系统设计之初就考虑到负载平衡,同时做好以下工作:

  • 检查现有系统是否存在明显短板,容易触发过载;
  • 系统与外部系统之间要做好入口流量控制;
  • 系统内“玻璃体质”服务的模块调用方要有错误抑制、熔断、降级等机制;
  • 关键服务的负载要实时监测、评估,并适当提高冗余资源;
  • 负载保护要与监控紧密结合,尽早发现系统异常,及时触发自动扩容;
  • 负载保护触发时要采访业务侧提供的策略;
  • 根据系统运营结果,及时发现系统在设计和运营方面的不足。

在流量管理方面,业界还出现了一个新的“风向标”——Istio,其理念是将业务中的很多微服务治理功能下沉到基础设施。这个设计理念是相当有创造力的,其实从软件架构设计的角度来看,这些思想方法并不独特,我们或多或少都曾应用过,例如集群入口流量管理与 Istio 南北向通信,集群内弹性熔断与 Istio 东西向通信,集群内脆弱服务的保护与 Istio 的智能代理,流控实现中数据和控制分离与 Istio 中数据平面和控制平面分离。但需要注意的是,再安全健壮的基础设施也挡不住业务“自杀式”设计,业务只有科学合理设计,才能与基础设施相得益彰,一起发挥整体效能。

嘉宾介绍

朱龙云,腾讯科技增值服务部互娱 AMS 监控系统负责人。2007 年加入腾讯,曾负责腾讯网网站广告发布系统,从事 CPD/CPC/CPM 等形式广告发布和运营,在广告 / 监控 / 流控等领域拥有多项专利。现就职于腾讯游戏增值服务部,曾参与开发过 Pandora 手游营销系统,支持手游时代新营销形式;主持设计和实现 AMS 系统全局负载保护系统,保证微服务系统在流量突变时安全运。当前主要负责 AMS 游戏营销监控系统,从事现网故障分析、弱阈值设置监控、主动探测系统和异常检测等方面工作。对分布式计算,大数据和函数式编程等领域比较感兴趣。

QCon北京2020的演讲中,你将从朱龙云老师的分享中了解到:微服务环境上游流控/本机负载保护/下游熔断方案相结合的整体系统负载保护方案;微服务环境下监控设计的方案,与传统监控差异,以及未来可能的发展方向。点击了解详情