导读:百度Feed信息流推荐系统服务于手百、好看、全民、贴吧等公司绝大多数信息流业务场景,随着业务的高速发展,整个系统承载的流量已经高达数十亿,在庞大的流量规模背后是数百个微服务和数万台机器作支撑。如何保证整套系统对外的高可用性是整个系统能力建设的关键,也是咱们团队的一个很是核心的工做方向。为了保障信息流推荐系统常态5个9的可用性目标, 本文将基于咱们实际的工做经验分享介绍百度Feed在线推荐系统是如何建设高可用性架构的。
1、背景
算法
百度Feed信息流推荐系统服务于公司绝大多数信息流业务场景,目前已经承载了手百、好看、全民、贴吧等多端产品的信息流推荐能力。以下是从资源漏斗维度给出的百度feed推荐系统的一个简化示意图。后端
△图1:系统简化示意图缓存
系统简要说明以下:网络
资源索引库:基于倒排索引或是向量索引提供数千万资源的索引查询能力,一般会有数十甚至数百的不一样的资源索引库,分别提供不一样类型的资源索引能力。架构
召回层:从数千万的资源中召回跟用户相关的数十万的资源,一般与资源索引库强绑定,好比一些显式召回会在召回层基于用户的tag去相应倒排索引库找到用户相关的文档资源。app
排序层:在大型推荐系统中,为了平衡算力和效果会构建粗排&精排两层排序体系,基于统一的预估打分机制从数万的资源中找到用户最感兴趣的数百条资源送给混排层。框架
混排层:混排层从数百资源中根据用户上下文信息推荐出最终用户看到的十几条资源,一般也会耦合产品的干预能力,好比资源的多样性控制等。运维
用户模型:用户模型服务提供用户级别的特征获取能力,好比用户感兴趣的标签集合。异步
文档特征:文档特征服务提供文档级的全特征服务,一般基于文档id访问获取获得。ide
随着业务的高速发展,百度Feed推荐系统目前已经承载了数十亿的在线请求流量,在庞大的流量规模背后是数百个微服务和数万台机器作支撑。如何保证系统的高可用是整个系统架构设计的关键能力之一,也是咱们整个团队工做的一个核心方向。
为了保证在线推荐系统常态5个9的高可用目标,基于咱们实际工做的不断抽象沉淀,造成了咱们整个架构的柔性处理能力。
△图2:多层级柔性处理能力
如图2所示,从对单请求的长尾超时异常到IDC级大规模故障,基于咱们的实际工做总结,都造成了相应的架构设计方案来应对,接下来进行具体的介绍
在线推荐系统是由数百个微服务所组成,而且在私有云上进行了大规模的混部, 对于常态可用性指标影响最大的因素主要有两个方面:
单实例故障所引发的可用性抖动,可能的引起缘由包括机器死机、磁盘打满,混部致使的cpu过载等。
长尾请求超时所致使的失败, 这一般是因为系统的一些随机因子所致使,引起的缘由多种多样,且很难被抽象归一,好比资源的竞争、接收流量的瞬时不均、周期性的内存清理、旁路日志操做等运维活动。
一般对于这类问题的解决方案是增长重试机制和对于异常实例的屏蔽探活机制,咱们的解决思路相似,但在实现方面对可能引起的问题作了进一步演进。
1.1 动态重试调度
重试机制最大的问题是重试时间的设定和雪崩问题:
重试时间设置的短,意味着常态下重试流量的比例会相对较大,形成资源的浪费,同时会更容易引起下游服务的雪崩,好比一旦下游出现大面积的延迟退化,就会致使流量的总体翻倍,进而整个下游服务都将被拖垮。
重试时间设置的长,意味着整个服务的超时都会长,不然重试的效果就起不到有效的做用,在复杂的微服务调度链路上很容易引发超时的倒挂。
固然重试时间的设定,咱们能够基于某个状态下去权衡利弊,给出一个近似的解来避免上面的问题, 但业务的快速迭代须要咱们不断的基于新的条件去更新这个值,这会带来极大的运维成本。
基于上述问题的考虑,咱们设计并实现了动态重试调度机制, 其核心思想是严格控制重试流量的比例(好比只容许3%的流量触发重试),同时基于分位耗时的实时动态采集机制,将重试的机会分配给须要重试的流量,这同时也意味着咱们再也不须要手动去维护超时时间的设定了。
以下是一个实现对比图:
△图3:动态重试调度
其核心实现包括:
后端分位耗时统计机制:将时间序列划分为周期性的间隔(好比20s一个周期),基于前一个周期的流量去统计分位耗时值,并结合配置化的重试比例来动态的肯定backup_request_ms的值,基于rpc的请求级重试时间设定机制,就达成了咱们动态超时调度的目标了。
熔断控制机制: 从分位耗时统计机制来看,能够发现这里有个周期的滞后, 但不会对咱们效果产生大的影响,这是由于先后两个周期服务的延时变化不会发生很大的波动,只要可以处理极端状况下滞后所致使的流量超发(可能引起的雪崩)问题就能够了, 而这正是熔断控制机制所要解决的问题, 熔断控制机制经过实时统计重试请求数量,并基于更小时间窗口来控制请求的比例,同时会经过平滑的机制去处理小窗口带来的统计不许问题。
整个方案在推荐系统应用后,在常态下可用性相对可以提高90%以上。
1.2 单实例故障处理
应对单实例故障一般采起的方案是屏蔽和探活机制,但在高可用架构上面还面临以下挑战:
识别能力上:这里包含几个维度的考量,准确率、召回率和时效性三个方面, 一方面基于单机视角的探活在时效性方面虽然可以作到实时的效果,但因为局部视角的缘由准确率每每不高,误识别的几率较大,而基于全局信息的采集每每会带来时效性的大幅下降,同时在性能方面也会带来必定折损。
策略处理上:一方面探活流量会致使小部分流量的折损,同时非全局视角的屏蔽会致使可用集群的服务容量风险,从而带来总体的恶化。
基于上述问题的考虑,咱们在动态重试调度的基础上,作了进一步的演进, 其核心思想在于实时动态的尽量下降损失,异步准实时的作全局异常处理控制。
实时动态止损:动态重试机制应对的场景是无实例差别的长尾请求,为了应对单实例故障这个特定化的场景,这里有两个抽象的处理方案来解决,其一基于可用性和耗时反馈的权重调节机制,利用可用性能够感知到异常实例的存在,从而快速下降权重访问,同时在重试实例的选择上也会加以区分;利用耗时反馈的平滑调节,会基于压力去自适应的调节正常实例的权重,保证正常实例的可用性不会由于容量问题打垮。
全局准实时的止损:基于咱们集成在brpc框架内部的组件可以快速的收集单机视角的实例级信息,而后周期级别的去上报给统一的控制汇聚层。 经过高效的代码实现以及两层的汇聚机制基本上能够作到对client的性能无影响。更重要的是咱们基于与pass的联动来进一步控制咱们对异常实例的控制策略,从而在保证可用性容量的前提下作完全的止损。
△图4:全局准实时止损方案
基于上述方案的实现,咱们的系统基本上可以自动的应对单实例故障的问题,可用性抖动相较于单纯的动态重试方案,抖动区间更小,基本上在5s内就能获得收敛,同时在抖动区间可用性的下降幅度也相较于以前降低了50%。
这里讲的服务级故障指的是在微服务体系下某个子服务出现大规模不可用,基本上没法对外提供任何服务能力。目前基于云原生的微服务化架构是整个业界的发展趋势,设计良好的微服务体系可以极大的提升整个系统开发的迭代效率,同时在稳定性方向也提供了更多可探索的空间。在微服务架构的基础上,咱们经过柔性架构的设计思想并结合多层级的容灾设计来应对这种大规模的服务故障场景。这里先给出其核心要点:
差别化对待异常:好比对于核心服务,咱们须要有必定的机制应对可能潜在的异常;对于非核心的服务,咱们要作的可能仅仅是控制它的异常不要扩散到核心链路。
多层级容灾机制:经过多层级的容灾机制,当大规模故障发生时,可以在必定的时间内保障用户体验的基本无损。
固然服务级故障的处理须要依托于具体的业务场景来作具体的设计,这里给出部分例子来讲明在Feed推荐系统是如何来进行处理的。
2.1 多召回调度框架
在推荐系统中,因为召回方式的差别性,每每会设计成多召回框架,每路召回负责某种具体的召回能力,好比从资源层面看,咱们能够分为图文召回,视频召回, 从召回算法层面,咱们能够分为itemcf召回,usercf召回等。就单召回路而言,对总体服务的影响取决于它的功能划分:
一级召回:好比运营控制相关的召回, 直接影响产品的体验感知, 不容许失败;
二级召回:决定了信息流分发效果,这些召回路若是失败,会致使大盘分发降低显著, 单路可容许短时间失败,但多路不能同时失败;
三级召回:补充召回路,好比探索兴趣召回,对长期效果起到必定增益做用,短时间对产品效果影响不显著,容许短时间失败。
对于三级召回路,服务调用失败虽然短时间对产品影响不显著,但大规模的超时失败会致使总体性能的延迟退化,进而影响到总体服务的可用性。一种直接粗暴的解决方案就是调小这些召回路的超时时间,使得其即便超时也不会影响到总体,但这显然会下降这些服务的常态可用性,不是咱们想要的效果, 为了抽象统一的解决这个问题, 咱们设计了多召回调度框架, 具体设计以下:
△图5:多召回调度框架
其核心设计包括:
召回等级划分:一级召回不容许主动丢弃;二级召回路划分多个群组,目前咱们按照资源类型划分,图文召回群组&视频召回群组&...;每一个召回群组由对分发贡献最大的几个召回路组成, 群组内部容许超过40%(可配)的召回路主动丢弃;三级召回路则容许主动丢弃;
丢层机制:当知足要求的召回路都返回后,剩下没有返回的召回路会主动丢弃,不在等待返回结果了;
补偿机制:丢弃对产品效果始终仍是有影响的,只是影响较小而已, 为了将这部影响下降到最小,咱们设计了旁路的cache系统,当主动丢弃发生后,利用上次的结果cache做为本次的返回,从而必定程度的下降损失。
基于上述方案的实现,经过咱们实际的演练结果,目前咱们整个系统能自动应对二/三级召回队列服务级故障的处理。
2.2 排序层故障解决方案
排序服务构建在召回之上,提供对多路召回数据的一个统一打分机制,从而完成资源的总体排序, 目前业界一般采用粗排&精排的两层排序体系来解决算力和效果相矛盾的问题。
粗排经过双塔模型的设计能在一次用户请求中支持数万的资源排序预估,但效果相对来讲偏弱;
精排经过更复杂的网络模型能提供效果更优的排序能力,但支持的预估数量级只有千级别;
召回 → 粗排 → 精排 的漏斗过滤体系在算力必定的状况下很好的提高了推荐效果。
能够看到,排序服务对整个推荐效果相当重要,一旦排序服务出现大规模异常,基本上就是在数万的资源里面进行随机推荐了。为了防止这类问题的发生,在设计之初咱们也是充分考虑了故障的应对场景,其核心思路是建设层级的降级机制。
△图6:排序层降级方案设计
其核心设计包括:
构建一层稳定的中间代理层Router:改层逻辑简单,迭代少,稳定性高,提供异常发生时的降级能力;
粗排异常处理方案:旁路建设一路基于资源的点展比和时长信息的排序能力,数据由离线后延统计信息挖掘获得,当粗排服务发生大规模异常时,利用该路信息提供应急排序能力;
精排异常处理方案:粗排的排序效果相比精排要差,但能够做为精排异常的时候降级数据供整个系统使用,当精排服务发生大规模异常时,直接利用粗排服务进行降级。
相比于随机推荐来讲,整套排序层故障解决方案可以大幅下降故障时对推荐效果的影响。
2.3 弹性容灾机制
基于排序层故障解决方案的进一步演进,咱们构建了针对全系统的弹性容灾机制,其目标是用一套统一的架构支持异常的处理,当异常发生的时候,下降对用户的体验感知和策略效果的影响。
△图7:弹性容灾新架构
其核心设计包括:
稳定的推荐系统入口模块Front:该模块是推荐系统的入口层,定位是不耦合任何业务逻辑,只支持弹性容灾相关的能力;
分层的异常数据建设:个性化容灾数据 + 全局容灾数据, 个性化容灾数据一般基于跨刷新数据的缓存来构建, 全局容灾数据经过旁路挖掘来构建;
统一的异常感知机制:跨服务的异常透传能力,当上层Front感知到核心服务异常时,就优先获取个性化容灾数据来进行容灾,若无个性化容灾数据,即访问全局容灾数据来进行容灾。
咱们经过信息流的置顶数据的异常处理来作说明。
全量容灾数据的挖掘:旁路周期性的将全部可用的置顶数据写入全局的容灾cache中;
个性化容灾数据的构建:当用户前一刷的时候,置顶召回服务会取出符合用户条件的最佳置顶数据集合,将这部分数据写入个性化容灾数据cache;
服务失败感知:当置顶召回访问失败,或是从Front到置顶召回服务之间任何链路失败时, Front接收到的数据返回包里面都没有标志置顶数据成功的标记;
容灾控制:Front会基于返回数据作判断,如没有成功标记,则开始进行容灾处理,优先读取个性化置顶容灾数据,没有读取到,则开始利用全局容灾数据进行容灾。
该机制构建了统一的弹性容灾能力,一方面利用个性化数据在容灾时尽量保证策略的损失最小,另外一方面利用全局的容灾数据进行兜底容灾尽量的保证用户体验的无感知。
IDC级的故障解决方案一般采用异地多主方案来解决,即故障发生时经过idc切流来及时止损, 百度推荐系统也采用相似的方案来解决,这里咱们经过下发历史存储服务的异地多主方案设计来作个介绍。
下发历史存储服务提供用户级别最近的下发、展示、点击过的资源。基于它提供跨请求的策略支持,好比多样性控制, 更重要的是防止重复资源的下发。一旦服务挂掉将整个推荐服务将没法对外提供服务。
其总体设计以下:
△图8:下发历史存储服务多主架构
其核心设计包括:
每一个地域维护一份全量的存储;
对于读请求,只容许读本地idc;
对于写请求,同步写入本地机房,同时进行跨idc同步写入,若写入失败,则写入消息队列,由消息队列进行异步跨idc进行写操做更新。
当出现idc级故障时,基于异地多主多活架构可以快速支持切流止损。
百度Feed推荐架构支持着业务的高速迭代发展,在整个架构演进过程,可用性建设一直是系统架构能力的关键指标,咱们经过柔性架构的建设,保证了对外常态5个9的高可用目标,并具有应对常规大规模故障的能力。
△图9:近一个月的出口可用性指标
本期做者 | windmill & smallbird
阅读原文:https://mp.weixin.qq.com/s/sm8eehjoUEqvF_PS4VcRKg
推荐阅读
|百度信息流和搜索业务中的弹性近线计算探索与应用
|有趣!有料!有温度!「百度技术沙龙」全新升级,重磅回归!
---------- END ----------
百度架构师
百度官方技术公众号上线啦!
技术干货 · 行业资讯 · 线上沙龙 · 行业大会
招聘信息 · 内推信息 · 技术书籍 · 百度周边
欢迎各位同窗关注!