百度评论中台为百度系产品提供便利接入、持续稳定的评论能力,是百度社区氛围体系内最重要的基础能力之一,日均流量达到百亿规模,在业务不断发展过程中,百度评论中台实现了功能快速迭代、性能稳步提升,本文将从整体介绍百度评论中台的架构设计,同时结合具体案例讲述如何构建高可用、高性能的分布式服务。
— 1 —
背景
评论作为用户主动表达提供情绪/态度/观点的重要方式之一, 其产品形态是以资讯作为载体,建设生产-分发-浏览的用户内容生态,最终促进内容与用户,用户与用户,用户与作者之间的价值交互氛围。
在阅读过一条资讯后,用户往往会浏览到评论区,或是抒发内心情感,或是寻找一些共鸣,也可能只是满足吃瓜群众的好奇心,有吸引力的评论区板块可以满足用户的互动诉求,同时也可以增加用户的产品粘性,促进业务发展;那么如何构建一套评论系统,在业务上能够支撑产品形态的多样化以及持续的迭代,在性能上能够提供稳定的能力输出,下面我们将会详细介绍百度评论中台的架构设计思路以及在业务发展过程中的一些挑战与探索。
— 2 —
概念介绍
评论从生产到读取历经多个环节,下图是评论生命周期中各个环节的简易模型:
评论数据的生产由用户触发,评论系统维护了与资源关联的主题数据,通过主题信息来检索归属的评论内容,评论数据本身维护了发表人,层级关系,是否展现状态,内容,点赞数,回复数,时间等等,每个数据维度用于特定的展现场景以及数据分析。
在用户生产内容过程中,系统识别恶意请求,针对用户、资源、内容等维度进行层层过滤,完成数据落盘,数据采用分布式数据库存储,同时根据查询维度进行垂直/水平拆库拆表,以解决业务持续增长带来的数据读写性能瓶颈,数据层面的变更最终落入大数据中心,用来分析业务现状以及未来的发展发向。
— 3 —
服务设计
上文铺垫了评论业务的相关概念,接下来我们侧重于介绍整体的服务设计,评论服务最初立足于百度App业务,为Feed等内容体系提供稳定便捷的评论能力,随着公司在中台方向上的战略转型,评论服务重新定位为评论中台,面向百度系的各个产品线,角色的转变,引发了架构设计方面的巨大改变,同时也带来了新的技术挑战:
从业务接入角度来看,接入业务方多,需求量大,定制化要求高,需要中台侧对外能够给予业务侧友好的快速接入,对于此部分需求,我们作为首个中台方完成了服务托管移动开发者中心(百度移动开发管理平台)的接入,借助该平台管理接口权限、技术文档、流量鉴权、服务计费以及售后咨询等能力,大大提升了接入效率。
对于中台侧内部,通过建设统一的接入层网关,实现容灾触发、流量染色、流量控制、异常检测等插件能力,既可以为业务提供便利的技术支持,在中台内部产生技术变更时,也可以做到业务无感知;
从能力输出角度来看,不同产品线的业务特点对于基础能力有着不同的诉求,这就需要中台侧对外能够满足业务需求,对内在架构设计中充分考虑通用化来保证有限的中台人力的研发效率。基于此,我们将接口能力不断抽象,在代码层面上保证低耦合、高内聚,针对不同的产品需求设计组件能力,专项解决痛点问题,同时我们也尝试针对特定的功能场景,提供友好的开发框架,通过与业务侧开源共建的方式来提升需求上线的效率,解放一定的中台人力,也取得了不错的效果。
从系统性能角度来看,接入流量随着新增产品线而日益增长,不断考验中台服务的性能,我们需要承诺核心接口SLA视角99.99%的服务稳定性。从架构上看,去除单点服务,逻辑机房内部形成隔离域,无跨地域请求,考虑极端场景,设计技术方案防止缓存穿透,缓存击穿,服务雪崩,保证服务的最小可用单元,从容量上看,保证高峰期容量满足N+1的冗余,具备单机房切空的能力,核心指标的报警覆盖至运维/研发/测试同学等等;
总体来看,建设思路就是,服务分层治理,不断沉淀中台通用化能力,对于产品和技术的升级,不断完善基础建设。
— 4 —
挑战与探索
上文以整体视角介绍了百度评论中台的架构设计,下面我们枚举了几个业务发展迭代过程中遇到的具体问题,来详细介绍下百度评论中台是如何进行技术探索并实施落地的。
如何提升服务性能与迭代效率?
背景:
一个系统在历经无数个版本迭代后,能够承载的功能越来越丰富,同时也会面临着开发成本日益增长的问题,业务逻辑夹杂着历史包袱,代码块混乱冗余,牵一发而动全身,这个时候对系统本身的技术升级便势在必行。
评论列表类接口是评论服务中的核心业务,历史上大部分的功能迭代列表类接口都会参与其中,这也促使了列表类接口更快地暴露出问题。
从下图优化前的业务逻辑处理流程不难看出,接口定义模糊,承载功能较多,相关的逻辑交织在一起,导致代码可读性不高;在业务处理部分,数据依赖比较多,不同的数据获取之间还存在着依赖关系,服务调用只能顺次执行,效率低下,这些问题会影响到日常开发的工作效率,并且在性能上也不可控,为系统稳定埋下了隐患。
思路:
面对这一系列问题,我们对列表类服务进行了一次大刀阔斧的改革,采用三条路径来解决核心问题:在接口层面,按功能拆分,定义单一职责的接口来解耦逻辑;在数据依赖层面,对下游调用进行调度管理,实现自动化,并行化;在数据渲染层面,采用流水线的装饰器来定义业务主线逻辑与支线逻辑,整体做到优化后的服务接口功能专一,代码边界清晰,依赖服务调度管理,数据打包灵活,可持续拓展,那么我们是如何实现这一过程的?下面介绍下具体的解决方案。
方案:
上图是一个接口下发数据的整体流程,总结起来可分为三个阶段:
第一阶段:作为接口的前置条件校验,对基础参数进行校验,过滤请求;
第二阶段:依赖数据的集中获取,这里是服务优化的核心部分,这里以评论一级列表接口为例,一级评论列表接口的数据下发,包括了一级评论,二级外漏评论,评论总数,主态可见评论数据,标签数据等等,每个数据源存在着一定的依赖关系。
比如,一级评论,主态可见评论,评论总数优先级最高,因为不依赖其他数据源便可以直接请求,二级外漏评论则需要一级评论数据获取到的父级评论列表作为基础依赖才能够执行,这样二级外漏评论的优先级次之,抽象来看,每个业务接口都可以梳理出自己的服务依赖关系,那么接下来的问题就变成如何将这些依赖关系管理、调度起来,这里我们采用了图引擎调度模型来完成这个任务:
该模型实现过程分为三个步骤:
第一步:定义,枚举业务逻辑中所需要的所有依赖服务,并映射成节点,每个节点是数据依赖调用的原子化节点,该节点中不参与接口维度的业务逻辑;
第二步:定义他们之间的关联,生成有向无环图,每个节点维护其依赖节点的个数,用入度值来表示;有向无环图的特点是节点与节点之间只有一个方向,不能成环,这种数据结构恰好符合业务节点之间的执行逻辑,把一个个服务依赖调用映射成有向无环图中的节点,每个节点的入度值来表示该节点的依赖节点数,其中顶点就是图中入度为0的节点,没有任何节点指向它;
第三步:由顶点开始,不断并发执行所有入度值为0的节点,上层节点弹出后,通过依赖关系寻找被依赖的节点,并对该节点的入度值做减操作,通过这种拓扑排序的结果输出,实现自动化的节点运行;
总结来说,将各个接口的节点关系进行配置化管理,当流量请求时,读取该接口的节点配置,生成有向无环图,节点的任务由调度器统一管理执行,每个节点的依赖数据输出会存储到当前请求的调度器中,到下游节点任务执行时,从调度器获取上游的数据结果作为入参,完成本节点的执行逻辑;
第三阶段:数据渲染阶段,通过阶段二获取到所有依赖数据后,从调度器中获取所有节点中的依赖数据结果,定义流水线式的格式化装饰器,按功能拆分,对响应值进行层层修饰,以实现数据渲染的模版化处理。
通过这种方式改造系统后,接口的服务性能大大提升,平均响应耗时在99分位维度上有了明显的降低,同时受益于Go语言的高性能,节省了物理机资源,重构后的代码可维护性也大为提升。
如何构建高性能、低延时的评论排序能力?
背景:
评论是生产用户内容的服务,那么对评论内容的运营则决定着整个评论区的氛围,对于优质/有趣或带有特殊属性的评论内容应该得到更多的曝光率,同时我们也希望低俗/辱骂/无意义的评论内容得到更少的关注,这样用户之间的互动才能得到正向的循环,这就要求评论服务构建一套排序机制,在产品层面上能够满足排序的需求,在技术上也可以做到排序数据快速收敛,不影响服务性能的前提下快速迭代。
思路:
在制定技术方案之前,我们需要考虑到几个核心问题,首先是如何对评论内容进行排序?
最容易想到的方案是为每条评论评估一个分值,按照分值的大小来输出一篇文章下的评论内容,以达到排序的效果;那么既然每条评论有了评分属性,接下来就要定义这个评分的公式,按照这个思路我们可以罗列出很多影响评论分值的因子。
比如一条评论的点赞数,回复数,创建时间等等,同时为这些因子设定相应的权重,组合起来可以计算出总分,在这个过程中,我们需要考虑公式的迭代给系统带来的性能冲击,当一个公式被确定下来,并不能马上推送到线上,而是需要小流量来评估排序结果带来的收益才能决定公式的因子或者因子权重是否是一组正确的组合,这便涉及到可能存在多组公式并存的情况,并且可以预料到公式的调整将会是频繁的迭代。
根据这些思路,我们最终开发出评论排序框架,采用离线计算评论分值,公式配置化,扇出并发模型等技术手段实现了智能化评论排序功能,下面我们详细看下架构设计。
方案:
评论排序服务分为两部分,离线粗排服务与在线精排干预。
首先介绍下离线粗排服务,我们将评估评论分值/输出排序列表的任务放在离线模块中执行,在数据量较大的情况下离线运算不会影响到线上服务。离线模块会监听评论行为队列,例如评论回复,评论点赞,评论删除,评论置顶等能够影响评论列表数据变更的行为,离线排序服务消费到行为数据后通过策略公式计算出评论分值,最终存储到排序索引中,这个流程涉及到的技术点如下:
单排服务;当一条评论数据发生变更时,触发单条评论分值的重新计算,判断是否需要触发全排服务,如果需要则将全排指令写入全排队列中,否责将最新的分值评估更新至排序索引中。
全排服务;从全排队列中读取全排指令,获取到评论所属的主题ID,遍历该主题ID下的所有评论,重新评估每条评论的评分,写入至排序索引中。此处使用了Golang语言的扇出模型,队列通过channel实现,管理多个协程并发消费channel的全排指令,减少数据消息的积压。
排序算子:在评估评论分值过程中,使用调度器并发获取排序因子所需要的数据,例如评论基础信息,主题物料信息,策略模型服务,提权/降权词表,重复内容库等,获取到的元信息通过排序公式的权重完成评估过程。
语义化公式:通过语义化公式识别,实现配置化上线,这里的考量是,线上的效果需要不断调整每个算子的权重来小流量验证,那通过这种方式可以实现快速上线完成验证,对研发效率及产品迭代都是非常高效;
排序索引:排序索引采用Redis作为存储介质,通过zset数据结构来维护一篇文章下的评论排序列表。不同的公式组成不同的策略,在多组策略同时生效时,排序索引会以文章维度生成多个策略的排序结果,同时维护多套ID索引,评论线上服务通过小流量读取策略组来命中不同的排序结果,方便AB实验。
离线粗排服务会针对评论的相关属性输出排序结果,在线部分,我们又对排序结果做了个性化的二次干预,包括个人评论提权,通讯录/关注好友评论提权,以及精排模型干预,这些策略帮助我们更为精准地定位用户群体的喜好,增加用户浏览评论的共鸣感。
如何保证服务的持续稳定性?
背景:
随着系统承载的业务流量越来越大,对服务稳定性建设方向上,评论团队也投入较大的资源/人力占比,一方面持续保证系统服务的高可用,不断提升系统容错能力,突发异常情况下能够快速应对,保障业务的最小可用单元,另一方面在业务逻辑反复迭代过程保证系统服务的高性能,不断提升用户体验。
稳定性建设是个持续优化的过程,我们通过不断探索,调研,选择适合评论服务的技术方案并落地,评论业务特点是面向C端用户,社会热点事件会对业务产生直接影响,会引发读写流量的突增,其中写流量的增加,对下游服务,下游存储会产生不小的负荷,同时,过多的写流量也会考验读流量的缓存命中率,一旦出现某一主依赖异常,整体服务将处于不可用的状态,针对这种风险,通过我们在热点,缓存,降级三个方面着手,为服务的稳定性保驾护航,下面看下具体实现方案:
缓存管理:
对于读流量的突增情况的处理方式通过设计多级缓存,提高缓存的命中率来处理,根据评论业务特点,我们设计了评论列表缓存,评论计数缓存以及评论内容缓存等,还使用了空缓存,来针对特定场景下大部分资源无评论的情况,防止缓存穿透。
在接受业务读流量请求时,首先使用LRU的本地缓存抵挡一波流量,查看是否能在最热最近的内存缓存列表中获取结果,本地缓存并没有命中,将会从Redis获取缓存,如果是突发热点,Redis的命中率很低,流量回源到db依然有很大的风险,所以这里使用了一层实例锁,从实例维度控制并发量,只允许一条请求透传到下游,其他请求等待结果,用这种方式来防止缓存击穿。
热点机制:
热点事件往往会引发流量的突增,热点事件产生时,通常会伴随着Push类的通知,更加引发单篇资源的集中式读写请求,而且这种热点事件产生时间比较随机,很难做到提前预判,单篇资源读写流量的升高,会导致缓存命中率下降,甚至缓存彻底失效,大量请求直接打到数据库,进而导致服务雪崩。
为了避免热点事件给系统带来不可控的冲击,我们设计了一套热点自动发现/识别系统:通过消息队列监听资源维度产生的评论行为,例如评论发表,回复,点赞,审核等等,通过计数来判定当前资源是否够热对评论行为进行计数统计。
当一篇资源下的评论行为持续增多,达到某一阀值(动态可配置)时,该资源被判定为热点资源,并将该资源推送至热点配置中心,命中热点的资源,其资源下产生的评论行为将写入热点队列,异步入库,同时,写操作后不再对缓存进行清理,而是重建缓存,来保证命中率。通过这一套机制,我们成功应对了一系列引发全民热议的热点事件,保证了用户体验。
容灾降级:
当出现极端的故障时,对系统的稳定性会产生巨大的影响,比如下游服务承受不住突增的业务流量,请求超时,实例问题引发的单点故障,机房网络问题导致不可用等等,我们的服务每日承载着百亿级别的PV流量,几秒钟的服务不可用就会产生巨大的损失。
因此容灾降级能力是考验系统服务高可用的一个重要标准,我们在这方面做了一系列的措施来提升系统应对风险的能力,建设业务在异常状态下的最小可用单元。
在容灾机制上,容灾触发细分为主动与被动,当业务与到可用性毛刺抖动时,由接入层进行感知,自动进入被动容灾逻辑,写请求进入队列异步入库,读请求直接返回容灾数据,提升容灾数据使用率。
主动容灾与运维平台打通,实现按接口、按机房、按比例来判定服务是否降级成容灾状态;在依赖管理方面,梳理业务弱依赖,一旦发生某一依赖服务异常,可以直接对依赖进行摘除。
在混沌工程方面,为了能够应对线上各种突发状况,对服务进行故障设计、故障注入,并针对服务给予的反馈,制定相应的预案建设,并将故障演练例行化,定期检验系统能够承受的风险级别。
— 5 —
总结
百度评论中台发展至今,历经了角色定位/技术架构的转变与升级,不断探索应用创新,打造极致的用户体验,目前,评论服务为百度系20+的产品提供评论功能,峰值QPS达到40w+,日均PV达到百亿规模,同时能够保证SLA视角的接口稳定性在99.995%以上。在未来的发展规划中,百度评论中台在服务创新、中台建设、稳定性等方面还会继续深造,助力建设优质的百度社区氛围。