网易公开课feed流架构的设计与实现(干货)

seoxin 01-16 21:00 20次浏览

“feed流”通常是指用户通过主动拉或者被动推的方式获取订阅信息流。常见的,比如微博上的超话,新版本的微信公众号订阅消息,抖音里的视频流等等。目前市面上主流的app首屏基本都是feed流设计。本文主要是从业务特征出发,深入浅出的介绍feed流的架构设计是如何结合公开课的业务特征做到高效落地的。

从业务架构看,“feed流”在纵向上可以拆解成三个部分:

  • 源内容引入
  • 源内容到feed内容过渡
  • feed内容投放

需求和挑战


在根据业务架构做技术抽象的时候,整个架构可以归纳收敛到以下几个技术点:

  • 如何做好内容引入的过滤策略
  • 如何保证内容存储架构的高可用
  • 如何做到内容个性化精准分发

内容架构总图


网易公开课feed流架构的设计与实现

设计与实现


(一)内容引入模型的设计与迭代

  • 内容引入模型v1.0如图2所示
网易公开课feed流架构的设计与实现

网易公开课的内容来源有两种:

  • 自运营的精品内容
  • 签约的第三方pgc用户

自运营的精品内容以及pgc签约用户生产的内容经过“机器审核”、“人工初审”、“人工复审”、“抽检”、“巡查”等基本审核流程,再过滤涉黄、涉政等敏感信息然后流入内容池。对于初期的公开课来说,这种模型虽然简单粗暴,但是确实也足够支撑业务。不过随着业务体量的增加,尤其是用户量的暴增,这个模型的缺点也暴露出来——即模型产出内容的速度远低于用户消费的速度。直白的说,这个模型的内容产出率是不足以支撑现有的用户规模消费的。

  • 内容引入模型v2.0如图3所示
网易公开课feed流架构的设计与实现

在2.0里,核心是引入了“签约入驻自媒体”,着重解决了内容产量问题。类似百度的“百家号”,头条的“头条号”,微信的“公众号”,网易新闻门户面向普通用户推出的发布内容的平台,广泛引入用户的内容灌入到签约自媒体里。然后业务自己再通过“漏斗模型”加权条件限制,过滤掉不符合公开课业务特征的内容。举个简单的例子,比如我们在引入学习音乐的视频时,通过制定的过滤规则:

  • 点赞数超过1w的
  • 标题包含音乐等关键词的
  • 有效回复数超过一定量的

在实际生产环境里,通过这一系列的过滤规则就能过滤掉大部分的杂质内容,然而机器审核肯定会有漏杀、误杀的bad case。基于这个考虑,我们新引入了一个“等级池”的概念,如图4。

网易公开课feed流架构的设计与实现

自运营内容 –> 精选池

签约pgc内容 –> 优质池

签约入驻自媒体内容 –> 普通池

“等级池”概念的引入主要是在首页个性化推荐算法以及服务降级兜底的时候用到,主要是保证服务体验以及保证服务稳定性的。

(二)内容存储架构设计

网易公开课feed流架构的设计与实现
  • 根据用户行为特征以及公开课的内容属性,在三个基本内容池基础上,再抽象出若干个属性分类池bucket,如图6。
网易公开课feed流架构的设计与实现

根据内容主体账号的属性自动map到具体的属性分类池,有点类似于bucket的概念,主要是把内容chunk到各个子bucket里。我们细化这么多内容池其实核心目的就是能更好的提取用户的行为特征。能更精确的获取到用户的行为,就能做出更准确的用户画像,而这里设计的 “账号“ ”内容“的映射关系,会写入到用户刻画&内容推荐算法系统里。用户在app上的行为会上报到用户刻画&内容推荐算法系统做训练。

  • 经过预处理后,开始构建底层数据持久化的存储架构,如图7
网易公开课feed流架构的设计与实现

2.1数据垂直拆分

假设每一条数据都有三个基本属性:

  • 课程基本内容(标题,简介等)
  • 用户动作行为(点赞,收藏等)
  • 内容个性化(分池、分类等)

在存储的时候,每条数据被垂直拆分成3块,垂直拆分,各自独立存储,通过业务特征内联,如图8

网易公开课feed流架构的设计与实现

至于为什么要垂直拆分而不做冗余?主要基于以下考虑:

  • 数据块的增量、访问量和更新量差距很大,qps不均衡,有大字段的更新查询问题
  • 基于模块和服务独立的思考,从数据维度做隔离
  • 基于数据特征考虑,独立的数据仓库更方便做数据淘汰,冷热分离

2.2数据水平拆分

水平拆分其实就是很常见的分库分表,公开课的用户行为数据有很多,像常见的收藏、点赞、观看历史等等。这些属性有些是数仓统计需要使用的,有些是推荐的算法模型需要用到的,还有些则是纯用户行为的数据。所以在业务导向上,水平切割成多个用户行为数据表。

2.3构建ES索引,如图9

网易公开课feed流架构的设计与实现

引入ES是业务上的强需求。公开课ES设计分为前后台ES,前端ES主要是面向C端,满足用户的基本的搜索需求。后端ES主要是面向B端,满足运营人日常需求,比如feed流场景中经常出现的一个case,想从千万的数据中根据标题查出来所有带有“王者荣耀”的内容并下线。文本搜索的支持上,mysql有点力不从心

2.4构建业务缓存

公开课采用的是双层缓存,设计思路参考mysql的回表,如图

网易公开课feed流架构的设计与实现

一级缓存是业务特征缓存,比如分类关系。二级缓存是基础数据的缓存,比如课程详情。 举个例子,热销课程表A对应的数据有a,b,c。则热销课程表A在基础缓存里就存着a,b,c。具体曝光页或者回流页的时再根据a,b,c去底层缓存取。这样做的好处:1.避免数据层间相互冗余存储,像热销课程表A的存储当然也可以直接把内容存下来,但是冗余量太大,存储利用率太低;2.冗余在数据同步的时候是很痛苦的,首先如何保证mysql和redis的一致性,其次还要保证多个缓存层的数据一致。比如一个课程从A改成B,则只需要改底层数据层缓存

另外,公开课的db和redis同步场景有两种:


update db + del redis + redis加过期淘汰策略 + query 回写缓存


update db + 消息队列 + 异步淘汰|更新


从逻辑上这两个场景都有bad case,并非解决db-redis一致性的最佳方案。但是针对一致性的讨论是要结合一致性的业务时间轴去讨论的,比如有绝对一致、分钟级别一致,小时级别一致,天级别一致等等。像银行热点账户问题,肯定要是要求完全一致,而用户行为数据等就是弱一致了。公开课的业务场景对于一致性的要求没有那么高。比如一个pgc作者在其账户下新发布了一条数据,那么在app可见这条数据的时间差是分钟级别的而非秒甚至毫秒级别一致同步的。再比如一个账号维度的统计,比如关注人数,内容数甚至允许到小时级别。而在设计缓存key过期时间的时候,会考虑到这一点,比如账号维度的key一个小时可能就会淘汰掉

2.5存储服务选型

mongo

只insert、select,可降级,可失败,可丢失

ddb存储大表、巨增量数据,以及垂直拆分的大字段,比如文章文本

mysql基本关系型业务数据redis双层缓存设计,存储核心曝光数据,top,hot数据ES

业务特征文本数据

2.6服务化拆分和负载均衡

如图1,图5所示,公开课整体内容架构调用链路非常长,而且从内容引入,到数据持久化,到缓存种植,最后到推荐模型,模块功能相对独立,所以在设计上根据各功能独立成子系统,通过中间件相互承载上下游服务。另外,由于各子系统内部有很多瞬间流量冲击的业务场景。比如,在特殊时期国家广电总局要求要下架所有包涉政内容。假设100个账号,每个账号下有1000条内容。那就要求10w的数据量要做到缓存下线、db下线、es下线、搜索索引下线、推荐模型下线、C端用户行为数据下线、账号内容通道下线等,一条数据的下线可能涉及到10表,10w的数据量因为滚雪球效应会引起下游qps暴增,真正落在db上的qps压力会被放大几倍甚至几十倍,造成流量尖峰。所以,设计上在做好服务拆分的同时,也要考虑到因为分布式系统引入的下游流量暴增而冲垮db的风险,目前公开课针对这种业务场景有两种思路:

  • db流量堆积,下游以主动拉的模式做周期性策略消费,这样db有“喘息”的时间,不至于打死。缺点就是有点慢,db流量消化时间久
  • 扩容、增加db实例去硬扛db流量

(三)个性化内容下发

前面几步其实是把源数据经过各种处理,最后放到某个bucket里。但是在APP里,如何做好用户维度的个性化推荐呢?换句话说,如何把用户精准的投放到某个bucket呢?

公开课采用的是pull模型。如图11

网易公开课feed流架构的设计与实现

同时公开课这边针对这个模型跟业务做了一些贴合。

  • 内容维度上打散的更细,比如前文提到的bucket足够多,采集项更细,模型分析的准确度会更高
  • 引入“等级池”的概念,保证个性化推荐模型的冷启动时(即无用户行为数据)和推荐服务故障时(图11中优化推荐模型停服时),直接取最优数据,做好服务兜底
  • 引入用户主动反馈功能。一般而言app都会有用户行为采集功能,通过app埋点等被动上传到用户行为服务里。公开课这边除了被动的上传反馈,还引入了用户主动反馈,比如在某一屏里刷到了一个bad case,用户可以在app上选择主动反馈

架构痛点


  • 内容同步的痛点。从架构图上能够看到公开课的数据流转路径是比较长的,而内容在每一个流转节点都有可能被修改状态,尤其是内容更新或者删除消息如果在某个节点丢了,再找回来比较麻烦
  • 从内容存储结构上,能够看到公开课的底层数据存储是比较离散的。这种离散的设计本意是尽量减少底层数据的冗余,但是缺点也很明显,想要一条完整的内容数据需要联多张表查询,尤其当数据量上去后,查询性能会逐步下降。尤其是涉及高复杂度的排序分页时,这个缺点尤其明显
  • 业务体量上去后,一般都会考虑到微服务的拆分。而我们一般拆独立服务的时候,也是把一类业务收敛到一起。按照目前的架构拆服务,有太多独立的service。Service过多势必会导致调用链路过长,引起很高的运维和维护成本。

总结


基础业务架构的运维是一个持续演进的过程,把一个成熟的技术架构跟业务做贴合,不能生搬硬套,需要对业务特征有比较深的理解。更重要的是业务架构并非一蹴而就,版本在不断迭代,技术在不断更新,技术栈与业务体系的结合点也要不断调整。永远没有最好的架构,只有最适合业务的架构