微信后端服务架构及其过载控制系统DAGOR

时间:2022-02-14 15:02:21

微信架构介绍

 

眼下的微信后端包含3000多个移动服务,包括即时消息、社交网络、移动支付和第三方授权。该平台每天收到的外部请求在10 ^10个至10^11个。每个这样的请求都会触发多得多的内部微服务请求,因而微信后端整体每秒需要处理数亿个请求。

 

微信的微服务系统容纳在微信业务系统中的20000多台机器上运行着3000多个服务,随着微信变得极其流行,这些数字在不断增加......随着微信不断发展,其微服务系统一直在经历服务更新的快速迭代。比如说,从2018年3月到5月,微信的微服务系统平均每天要经历近千次变更。

 

微信将其微服务分成这几大类:“Entry leap”服务(接收外部请求的前端服务)、“Shared leap”服务(中间层编排服务)和“基本服务”(不向任何其他服务敞开的服务,因而充当请求的接收器)。

 

微信后端服务架构及其过载控制系统DAGOR

微信的微服务架构

 

在平常的一天,峰值请求率是每日平均值的3倍左右。在每年的某些时段(比如农历新年前后),峰值工作负载会激增至每日平均值的10倍。

 

为基于微服务的大规模平台设计过载控制系统面临的挑战

 

过载控制......对于尽管面临任何不可预测的负载激增情形,仍需要实施24×7服务可用性的大规模在线应用而言至关重要。

 

传统的过载控制机制是为拥有少量服务组件、比较窄的“前门”(front-door)和平凡依赖项的这种环境设计的。

 

……现代在线服务在架构和依赖项方面正变得越来越复杂,远远超出了传统过载控制的设计用途。

 

  • 由于对发送到微信后端的服务请求而言没有单一的入口点,在全局入口点(网关)实行集中式负载监控这种常规方法并不适用。

  • 某个特定请求的服务调用图可能依赖针对特定请求的数据和服务参数,即便对于同一类型的请求也是如此。因此,某个特定服务变得过载时,很难确定应丢弃哪些类型的请求以缓解这种情况。

  • 过多的请求中止(处于调用图的更深处或请求处理的更晚期时尤为如此)浪费了计算资源,并因高延迟而影响了用户体验。

  • 由于服务DAG极其复杂且不断发展,因此有效的跨服务协调所需的维护成本和系统开销过高。

 

由于一个服务可能对它依赖的某个服务发出多个请求,还可能对多个后端服务发出请求,因此我们在过载控制方面格外小心。论文作者为多个过载服务被调用或单单一个过载服务被多次调用的情况首次提出了后续过载(subsequent overload)这个术语。

 

后续过载给有效的过载控制提出了挑战。直观上来讲,服务过载时随机地执行减载(load shedding)可以使系统经受得住饱和的吞吐量。然而,后续过载可能使系统吞吐量大大降低,超出预期......

 

考虑一个简单的场景:服务A两次调用服务B。如果B开始拒绝所有入站请求中的一半,A的成功率就降至0.25。

 

DAGOR概述

 

微信的过载控制系统名为DAGOR,它旨在为所有服务提供过载控制,因而旨在与服务无关。由于集中式全局协调的成本过于高昂,过载控制在单台机器的细粒度层面来运行。然而,它确实包含一种轻量级协作式机器间协议,处理后续过载情况需要该协议。最后,因过载而导致减载不可避免时,DAGOR会维持服务的尽力(best-effort)成功率。应尽量减少耗费在失败的服务任务上的计算资源(比如CPU和I/O)。

 

我们有两个基本任务要解决:检测过载情况,检测出来后决定如何处理。

 

  • 过载检测

 

针对过载检测,DAGOR使用了待处理队列中请求的平均等待时间(即队列时间)。队列时间的优点在于抵消了调用关系图(call-graph)低层次中的延迟带来的影响(与比如请求处理时间相比)。即使本地服务器本身没有过载,请求处理时间也会增加。DAGOR使用基于窗口的监控,一个窗口对应一秒或2000个请求,先到先处理。微信显然实行了严格的管理:

 

针对过载检测,鉴于微信中每个服务任务的默认超时为500ms,表明服务器过载的平均请求队列时间的阈值被设置为20ms。这种单凭经验的配置已经在微信业务系统中用了五年多,微信业务活动方面的系统稳健性已证实了其有效性。

 

  • 准入控制

 

一旦检测到过载,我们就必须决定如何处理它。或者更直言不讳地说,我们要丢弃哪些请求。第一个结论是,并非所有请求都一样(重要):

 

微信的操作日志显示,微信支付和即时消息遇到类似的服务不可用时段时,用户对微信支付服务的投诉是对即时消息服务的投诉的100倍。

 

为了以一种与服务无关的方式处理这个问题,每个请求在首次进入系统时都被赋予业务优先级。该优先级与所有下游请求一块传送。用户请求的业务优先级由请求的操作类型决定。虽然有数百个入口点,但只有几十个有显式优先级,其他所有入口点都有默认(较低)优先级。优先级保留在一张复制的哈希表中。

 

微信后端服务架构及其过载控制系统DAGOR

哈希表存储了在微信入口服务中执行的操作的业务优先级

 

过载控制被设置为业务优先级n时,来自优先级n+1的所有请求一律被丢弃。这对于混合工作负载来说很好,但是假设有潮水般涌入的支付请求,所有这些请求都有同样的优先级(比如p)。系统将变得过载,因此将过载阈值改为p-1,这时系统将再次处于轻负载情形。一旦检测到轻负载,过载阈值再次调高到p,再一次处于过载状态。为了在因同一优先级的请求而超载时停止这种翻转,我们需要比业务优先级更精细的粒度级别。

 

微信有一个很好的办法来解决这个问题。它增加了基于用户ID的第二层准入控制。

 

用户优先级由入口服务通过以用户ID作为参数的哈希函数动态生成。每个入口服务每小时更改其哈希函数。因此,来自同一用户的请求很可能在一小时内被赋予同样的用户优先级,但几小时内被赋予不同的用户优先级。

 

这提供了公平性,同时又让每个用户在一段比较长的时间内提供了一致的体验。它还有助于解决后续过载问题,因为来自被赋予高优先级的用户的请求更可能在调用图中一路得到优先处理。

 

结合业务优先级和用户优先级提供了复合准入级别,每个业务优先级有128个用户优先级。

 

微信后端服务架构及其过载控制系统DAGOR

复合准入级别

 

由于业务优先级的每个准入级别有128个用户优先级,因此所得的复合准入级别数量是成千上万。可在用户优先级这个层面调整复合准入级别。

 

详细的补充内容解释了为什么使用会话ID而不是用户ID不行:你到头来训练用户在遇到糟糕的服务时先注销再重新登录,现在除了原来的过载问题,你还多了登录风暴(login storm)!

 

DAGOR在每台服务器上都有一张请求直方图,跟踪请求相对准入优先级的大致分布。窗口期间检测到过载时,DAGOR进入到将预期负载减少5%的第一个桶(bucket)。由于没有过载,它进入到将预期负载增加1%的第一个桶。

 

服务器将当前的准入级别捎带在发送到上游服务器的每个响应消息上。这样一来,上游服务器知道下游服务的当前准入控制设置,甚至在发送之前就对请求执行本地准入控制。

 

因此,端到端的DAGOR过载控制系统如下所示:

 

微信后端服务架构及其过载控制系统DAGOR

DAGOR过载控制的工作流程

 

试验结果

 

DAGOR这种设计的最佳证明是,五年来它在微信的生产环境中一直顺畅地运作。不过这没有为学术论文提供必要的图表,于是我们还进行了一组模拟试验。下图表明了基于队列时间而不是基于响应时间的过载控制具有的好处。这些好处在后续过载的情形下体现得最为明显,见图(b)。

 

微信后端服务架构及其过载控制系统DAGOR

用不同的负载分析指标:队列时间vs响应时间来检测过载

 

与CoDel和SEDA相比,进行一次后续调用时,DAGOR在后续过载方面的请求成功率提高了50%。后续请求数量越多,好处越明显:

 

微信后端服务架构及其过载控制系统DAGOR

面对不同类型的工作负载下的过载控制

 

最后在公平性方面,可以认为CoDel在负载很大时优先处理向过载服务敞开较小的服务,而DAGOR面对各种请求时表现出了大致相同的成功率。

 

微信后端服务架构及其过载控制系统DAGOR

过载控制的公平性

 

可用于你自家系统的三个经验

 

即使你没有完全按描述的那样使用DAGOR,论文作者还是列出了三个值得考虑的宝贵经验:

 

  • 大规模微服务架构中的过载控制面对每个服务时必须是分散的、自主的。

  • 过载控制应考虑各种反馈机制(比如DAGOR的协作式准入控制),而不是仅仅依赖于开环启发式方法。

  • 应通过分析实际工作负载的处理行为,为过载控制设计提供事实依据。