系统设计面试:卷1 第11章:设计一个新闻推送系统

160 阅读9分钟

在这一章中,你被要求设计一个新闻推送系统。什么是动态消息?根据Facebook的帮助页面,“新闻动态是在你的主页中间不断更新的故事列表。News Feed包括状态更新、照片、视频、链接、应用活动,以及你在Facebook上关注的人、页面和群组的“赞”[1]。这是一个很常见的面试问题。类似的问题有:设计Facebook的消息推送、Instagram的动态推送、Twitter的时间轴等等。

image.png

第一步-理解需求并且建立设计范围

第一组澄清问题是为了理解面试官让你设计新闻推送系统时的想法。至少,您应该弄清楚要支持哪些特性。这里有一个面试官和面试官互动的例子:

应聘者:这是一个移动应用程序吗?还是网页应用?还是两个? 面试官:二者 求职者:什么是重要的特点? 面试:用户可以发布一篇文章,并在新闻提要页面上看到她的朋友的文章。

候选人:新闻提要是按时间倒序排序,还是按主题分数等特定顺序排序?例如,来自你亲密朋友的帖子得分更高。

采访者:为了简单起见,让我们假设提要是按时间倒序排序的。

候选人:一个用户可以有多少朋友?

面试官:5000

应聘者:流量是多少?

面试官:1000万DAU

候选人:feed是否可以包含图像、视频或文本?

采访者:它可以包含媒体文件,包括图像和视频。

现在您已经收集了需求,我们将集中精力进行系统设计。

第二步-顶层设计

设计分为两个流程:提要发布和新闻提要构建。

  • Feed发布:当用户发布文章时,相应的数据被写入缓存和数据库。一个帖子被添加到她朋友的新闻动态中。

  • 信息流构建:为了简单起见,让我们假设信息流是通过按时间倒序聚合好友的帖子来构建的。

消息推送API

新闻提要api是客户机与服务器通信的主要方式。这些api是基于HTTP的,允许客户端执行操作,包括发布状态、检索新闻提要、添加好友等。我们将讨论两个最重要的API:消息发布API和消息检索API。

消息发布API

要发布一个帖子,将向服务器发送一个HTTP post请求。API如下所示:POST /v1/me/feed参数:

  • content: content是文章的文本。

  • auth_token:用于认证API请求。

消息检索API

获取新闻提要的API如下所示:GET /v1/me/feed参数:

  • auth_token:用于对API请求进行身份验证。

提要发布

提要发布流程的高级设计如图11-2所示。

image.png

  • 用户:用户可以在浏览器或移动应用上查看新闻提要。用户通过API: /v1/me/feed?content=Hello&auth_token={auth_token}发布童谣

  • 负载均衡器:将流量分配到web服务器。

  • Web服务器:Web服务器将流量重定向到不同的内部服务。

  • Post服务:在数据库和缓存中持久化Post。

  • Fanout服务:向好友推送新内容。新闻提要数据存储在缓存中以便快速检索。

  • 通知服务:通知好友有新内容可用,并发送推送通知。

消息提要 建立

在本节中,我们将讨论如何在幕后构建新闻提要。高层设计如图11-3所示:

image.png

  • 用户:用户发送请求来检索她的新闻提要。请求看起来像这样:/ v1/me/feed。

  • 负载均衡器:负载均衡器重定向流量到web服务器。

  • Web服务器:Web服务器将请求路由到newsfeed服务。

  • 消息提要服务:消息提要服务从缓存中获取消息提要。

  • 消息提要缓存:存储呈现消息提要所需的消息提要id。

底层实现

高级设计简要介绍了两个流程:提要发布和新闻提要构建。

在这里,我们将更深入地讨论这些主题。

提要发布底层实现

提要发布的详细设计如图11-4所示。我们已经讨论了高层设计中的大部分组件,我们将重点讨论两个组件:web服务器和fanout服务。

image.png

web 服务器

除了与客户端通信外,web服务器还强制执行身份验证和速率限制。

只有使用有效auth_token登录的用户才允许发布帖子。该系统限制了用户在一定时间内可以发布的帖子数量,这对防止垃圾邮件和滥用内容至关重要。

广播服务

Fanout是向所有朋友发送帖子的过程。两种类型的fanout模型是:写时的fanout(也称为推型)和读时的fanout(也称为拉型)。这两种模型各有优缺点。我们解释了它们的工作流程,并探索了支持我们系统的最佳方法。

写Fanout。使用这种方法,新闻提要是在写入时预先计算的。新帖子发布后,会立即发送到好友缓存中。

优点:

  • 动态消息是实时生成的,可以立即推送给朋友。

  • 获取消息提要是快速的,因为消息提要是在写入时预先计算的。

缺点:

  • 如果用户有很多好友,那么获取好友列表并为所有好友生成消息就会变得缓慢且耗时。这被称为热键问题。

  • 对于不活跃的用户或很少登录的用户,预计算消息源会浪费计算资源。

读Fanout。新闻提要是在阅读时间生成的。这是一个按需模式。

当用户加载她的主页时,会拉出最近的帖子。

优点:

  • 对于不活跃的用户或很少登录的用户,阅读时的扇出效果更好,因为它不会在他们身上浪费计算资源。

  • 数据不会推送给朋友,所以没有热键问题。 缺点:

  • 获取消息提要很慢,因为消息提要没有预先计算。

我们采用混合方法来获得两种方法的优点并避免它们中的缺陷。

因为快速获取新闻是至关重要的,所以我们为大多数用户使用了推送模式

对于名人或有很多朋友/追随者的用户,我们让追随者按需拉新闻内容,以避免系统过载。一致性哈希是一种缓解热键问题的有用技术,因为它有助于更均匀地分发请求/数据。

让我们仔细看看fanout服务,如图11-5所示。

image.png

fanout服务的工作原理如下: 1.单击“确定”。从图形数据库中获取好友id。图形数据库适合于管理朋友关系和朋友推荐。有兴趣了解这一概念的读者可以参考参考资料[2]。

  1. 从用户缓存中获取好友信息。然后系统会根据用户设置过滤掉好友。例如,如果你把某人设为静音,即使你们还是朋友,她的帖子也不会出现在你的动态消息中。帖子不显示的另一个原因是,用户可以有选择地与特定的朋友分享信息,或者对其他人隐藏信息。

  2. 发送好友列表和新帖子ID到消息队列。

  3. Fanout工作程序从消息队列中获取数据,并将新闻提要数据存储在新闻提要缓存中。您可以将新闻提要缓存看作一个映射表。

每当有新的帖子发布时,它将被添加到新闻提要表中,如图11-6所示。如果我们将整个用户和post对象存储在缓存中,内存消耗会变得非常大。因此,只存储id。为了保持较小的内存大小,我们设置了一个可配置的限制。用户在新闻推送中滚动浏览数千篇帖子的可能性很小。大多数用户只对最新的内容感兴趣,所以缓存缺失率很低。

  1. 在新闻提要缓存中存储。图11-6展示了一个在缓存中新闻提要的示例。

image.png

消息提要检索实现

图11-7展示了新闻提要检索的详细设计。

image.png

如图11-7所示,媒体内容(图片、视频等)存储在CDN中,便于快速检索。让我们看看客户端是如何检索新闻提要的。

  1. 用户发送一个请求来检索她的新闻提要。请求看起来像这样:/v1/me/feed 2。负载均衡器将请求重新分配到web服务器。

  2. Web服务器调用新闻提要服务来获取新闻提要。

  3. 新闻提要服务从新闻提要缓存中获取列表帖子id。

  4. 用户的新闻提要不仅仅是一个提要id列表。它包含用户名,头像,帖子内容,帖子图像等。因此,新闻提要服务从缓存(用户缓存和帖子缓存)中获取完整的用户和帖子对象,以构造完整的新闻提要。

  5. 完整的新闻提要以JSON格式返回给客户端进行呈现。

Cache 架构

缓存对于新闻提要系统是极其重要的。如图11-8所示,我们将缓存层划分为5层。

image.png

  • News Feed:它存储新闻Feed的id。

  • 内容:它存储每一个帖子的数据。热门内容存储在热缓存中。

  • Social Graph:它存储用户关系数据。

  • 动作:它存储有关用户是否喜欢帖子,回复帖子或对帖子采取其他操作的信息。

  • 计数器:它存储计数器喜欢,回复,追随者,以下等。

第四步-总结或打包

在本章中,我们设计了一个新闻推送系统。我们的设计包含两个流程:提要发布和新闻提要检索。

就像任何系统设计面试问题一样,没有完美的系统设计方法。每个公司都有其独特的限制,你必须设计一个系统来适应这些限制。

理解设计和技术选择之间的权衡是很重要的。如果还有几分钟,您可以讨论可伸缩性问题。为了避免重复讨论,下面只列出了高级别的谈话要点。 扩展数据库:

  • 垂直扩展与水平扩展
  • SQL与NoSQL
  • 主从复制
  • 读取副本
  • 一致性模型
  • 数据库分片 其他要点:
  • 保持web层无状态
  • 尽可能多地缓存数据
  • 支持多个数据中心
  • 丢失消息队列的几个组件
  • 监控关键指标。
  • 例如,高峰时段的QPS和用户刷新新闻feed时的延迟都值得关注。