1。背景最近在研究过载保护。微信是月活跃用户超过10亿的国民级应用,在春节和节假日期间消息数量往往会急剧增加。服务容易超载,但是微信的服务一直比较稳定。他们是如何做到的呢?本文以微信在2018年Socc大会上发表的文章《Overload Control for Scaling Wechat Microservices》为基础,介绍了微信大规模微服务的过载保护策略,其中很多内容具有很大的参考价值。以下是对这篇文章的一些解读。二、过载保护的基本概念1、什么是服务过载?服务过载是指服务的请求量超过了服务所能承受的最大值,导致服务器负载过大,响应延迟增加,用户端性能加载不上或加载缓慢,会引起服务端的进一步重试用户,而服务过去一直在处理无效请求,导致有效请求降为0,甚至导致整个系统雪崩。2、为什么会出现服务过载?互联网天生突发流量、秒杀、抢购、重大突发事件、节庆甚至恶意攻击等,都会让服务承受数倍于平时的压力。微博经常有明星官宣结婚或离婚导致服务器崩溃。场景,这是服务过载。3、过载保护的好处主要是提升用户体验,保证服务质量。在突发流量的情况下,它仍然可以提供部分服务能力,而不是整个系统瘫痪。系统瘫痪意味着用户流失、口碑不佳、夫妻吵架,甚至危及生命(如果腾讯文档崩溃了,这个文档只是用来救灾)。3、微信中的过载场景微信使用了微服务,称为微服务。其实我的理解是它们是使用统一的RPC框架构建的独立服务。这些服务相互调用以实现各种功能。这也是现代服务的基本架构。毕竟没人愿意看到我的朋友圈崩塌,害得我没法聊天。微信上的服务分为三层,接入服务、逻辑服务、基础服务。大多数服务都是逻辑服务。登录、消息发送、支付等接入服务日请求量在10亿到100亿之间。期间,入口协议触发更多逻辑服务和基础服务的请求,核心服务每秒处理数亿个请求。在大规模的微服务场景下,重载会变得更加复杂。如果是单个服务,一个事件只需要一个请求,但是在微服务下,一个事件可能会请求很多个服务。如果任何一个服务没有被重载,那么其他所有的请求都是无效的。如下所示。比如在转账业务下,需要分别查询两者的卡号。当查询A成功,查询B失败时,查卡号事件失败。比如查询成功率只有50%,那么对于查询两人的卡号成功率只有50%*50%=25%。事件调用服务的次数越多,成功率就越低。4、如何判断过载通常判断过载可以使用吞吐量、延迟、CPU占用率、丢包率、待处理请求数、请求处理事件等。微信以请求在队列中的平均等待时间为准,即从请求到达到开始处理的时间。为什么不使用响应时间?因为响应时间与服务相关,很多微服务被链式调用,响应时间不可控,无法标准化,很难作为统一的判断依据。那为什么不用CPU负载作为判断标准呢,因为CPU负载高并不代表服务过载,因为一个服务请求处理的很及时,CPU高是比较好的表现。实际上,当CPU负载高时,监控服务会报警,但不会直接进入过载处理流程。腾讯微服务默认超时时间为500ms。通过计算每秒或每2000个请求的平均等待时间是否超过20ms,可以判断是否过载。这个20ms是基于微信后台5年摸索出来的阈值。使用平均等待时间的另一个好处是它独立于服务,可以应用于任何场景,不与业务关联,可以直接在框架上修改。当平均等待时间大于20ms时,采用一定的降速因子对部分请求进行过滤调整。如果判断平均等待时间小于20ms,则按一定比例提高通过率。一般采用快降慢升的策略来防止业务出现较大的波动,整个策略相当于一个负反馈回路。5.过载保护策略一旦检测到服务过载,需要按照一定的策略对请求进行过滤。前面分析过,对于链式调用的微服务场景,随意丢弃请求会导致整体服务的成功率很低。所以请求是按照优先级来控制的,优先级低的请求会先被丢弃。1.不同的业务场景,业务的优先级不同。比如登录场景是最重要的业务。如果不能登陆,一切都是白搭。此外,支付消息比普通消息具有更高的优先级,因为用户对金钱更敏感,但普通消息的优先级高于朋友圈消息,所以在微信中有天然的业务优先级。用户的每一个请求都会被分配一个优先级,在微服务的链式调用下,下游请求的优先级也会被继承。比如我请求登录,那么后续的一系列请求比如查看账号密码等都会继承登录优先级,这样就保证了优先级的一致性。每个后台服务维护一个业务优先级的哈希表。微信上的业务太多了,并不是每个业务都记录在表中,不在表中的业务优先级最低。2、用户优先级明显。仅基于业务优先级的控制是不够的。首先,不可能因为负载高就丢弃或者允许整个服务的请求,因为每个服务都有大量的请求,这肯定会造成负载的大波动,而如果在业务中随机丢弃请求,在过载条件下总体成功率仍然很低。为了解决这个问题,可以引入用户优先级。首先,用户优先级不应该相同。对于普通人来说,用户优先级是通过对用户的唯一ID进行哈希计算得到的。为了防止老是打豆豆的现象,hash函数每隔Hourly更换一次,就像业务优先级一样,单个用户的访问链的优先级总是一样的。这里有个问题,为什么不用sessionID来计算优先级呢?从理论上讲,使用sessionID和用户ID的效果是一样的,但是当用户再次登录时使用sessionID进行刷新,此时,用户的优先级可能已经改变了。在过载的情况下,他可能会因为提高了优先级而恢复,这样用户就会养成坏习惯,在服务出现问题时重新登录,这无疑会进一步加剧服务的过载。由于引入了用户优先级,与业务优先级形成了一个二维的控制面。根据负载情况,确定本台服务器的准入优先级(B,U)。当传入请求业务优先级大于B,或者服务优先级等于B,但用户优先级高于U,则通过,否则拒绝。3、自适应优先级调整在大规模微服务场景下,服务器负载变化非常频繁,因此需要动态改变服务器的访问优先级。微信划分了几十个业务优先级,每个业务优先级下有128个用户优先级,所以总的优先级有几千个。如何根据负载调整优先级?最简单的方式就是从右向左遍历,每次调整判断负载情况,时间复杂度为O(n),即使使用二分法,时间复杂度也是O(logn),在千优先级下正常在某些情况下,可能需要进行数十次调整才能确定适当的优先级。每次调整和统计优先级后,可能需要几十秒。这种方法无疑是非常低效的。微信提出了一种基于直方图统计的快速调整访问优先级的方法。服务器上maintainer当前访问优先级下,每个优先级在过去一个周期的请求量(1s或2000个请求),当过载时通过减少下一个周期的请求量来降低负载,假设上一个周期所有优先级的请求总和为N,下一个周期的请求量要减少N*a,怎么减少,每增加一个优先级如果减少某个级别requests的数量,它会增加,直到减少的数量大于目标数量,然后用相反的方法来恢复负载,只是系数是b,它比a小,它也是为了快减慢增的目的。根据经验,a为5%,b为1%。为了进一步减轻超载机器的压力,是否可以在下游超载时不向下游发送请求?否则下游还是要接受请求,解包,丢弃请求,白白浪费带宽,增加下游负载。为了实现这种能力,每次请求下游服务时,下游将当前服务访问优先级返回给上游,上游维护下游服务的访问优先级。如果发现请求优先级没有达到下游服务访问阈值,直接丢弃,不请求下游,进一步减轻下游压力。6.总结微信整个负载控制流程如图所示:当用户从微信发起请求时,请求被路由到接入层服务,并分配统一的服务和用户优先级,所有词请求到下游继承相同的优先级。根据业务逻辑调用一个或多个下游服务。当一个服务收到一个请求时,它首先根据自己的服务访问优先级来判断该请求是被接受还是被丢弃。服务本身会根据负载情况定期调整准入优先级。当服务需要向下游发起请求时,判断本地记录的下游服务访问优先级,如果小于该记录则丢弃,如果没有记录或者优先级是则向下游发起请求大于记录。下游服务返回上游服务需要的信息,并在信息中携带自己的准入优先级。上游收到返回后,解析信息,更新本地记录的下游服务访问优先级。整个过载保护策略具有以下三个特点:业务无关,使用请求等待时间代替响应时间,设置用户和业务优先级,与业务本身无关。独立控制和联合控制相结合,访问优先级取决于独立服务,但也可以结合下游服务的情况,优化服务过载时的性能。高效公平,请求链优先级一致,哈希函数会周期性变化,调整用户优先级。在过载的情况下,并不总是影响固定用户。
