深入解析新闻订阅系统的设计和实现

本文首发于公众号:更AI (power_ai),欢迎关注,编程、AI干货及时送!

在本章中,你被要求设计一个新闻订阅系统。什么是新闻订阅呢?根据 Facebook 的帮助页面,“新闻订阅是你主页中间的故事列表,它会不断更新。新闻订阅包括状态更新、照片、视频、链接、应用活动以及你在 Facebook 上关注的人、页面和群组的喜欢” [1]。这是一个流行的面试问题。常见的类似问题包括:设计 Facebook 新闻订阅,Instagram 订阅,Twitter 时间线等。

image-20230525202508214

第一步 - 理解问题并确定设计范围

第一组澄清问题是理解面试官在问你设计新闻订阅系统时的想法。至少,你应该弄清楚要支持哪些功能。以下是候选人与面试官交流的一个例子:

候选人 : 这是一个移动应用吗?还是一个网页应用?或者两者都有?
面试官 : 两者都有。

候选人 : 什么是重要的功能?
面试官 : 用户可以发布帖子,并在新闻订阅页面上看到她的朋友的帖子。

候选人 : 新闻订阅是按照逆时间顺序排序的,还是按照某种特定的顺序排序,比如主题得分?例如,你的亲密朋友的帖子得分更高。
面试官 : 为了简单起见,让我们假设订阅是按照逆时间顺序排序的。

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

候选人 : 交通量是多少?
面试官 : 1000万日活用户

候选人 : 订阅可以包含图片、视频,还是只有文本?
面试官 : 它可以包含媒体文件,包括图片和视频。

现在你已经收集到了需求,我们专注于设计系统。

第二步 - 提出高层次设计并获得认可

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

  • 订阅发布:当用户发布一篇帖子时,相应的数据被写入缓存和数据库。一篇帖子被推送到她的朋友的新闻订阅中。
  • 新闻订阅构建:为了简化,让我们假设新闻订阅是通过按逆时间顺序汇总朋友的帖子来构建的。

新闻订阅 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-20230525202541527

  • 用户:用户可以在浏览器或移动应用上查看新闻订阅。用户通过 API 发布一条带有“Hello”内容的帖子:
    /v1/me/feed?content=Hello&auth_token={auth_token}
  • 负载均衡器:将流量分配给 Web 服务器。
  • Web 服务器:Web 服务器将流量重定向到不同的内部服务。
  • Post 服务:在数据库和缓存中持久化帖子。
  • Fanout 服务:将新内容推送到朋友的新闻订阅中。新闻订阅数据存储在缓存中以便快速检索。
  • 通知服务:通知朋友新内容可用,并发送推送通知。

新闻订阅构建

在本节中,我们将讨论新闻订阅如何在后台构建。图 11-3 显示了高级设计:

image-20230525202600750

  • 用户:用户发送一个请求来检索她的新闻订阅。请求如下:/v1/me/feed。
  • 负载均衡器:负载均衡器将流量重定向到 Web 服务器。
  • Web 服务器:Web 服务器将请求路由到新闻订阅服务。
  • 新闻订阅服务:新闻订阅服务从缓存中获取新闻订阅。
  • 新闻订阅缓存:存储渲染新闻订阅所需的新闻订阅 ID。

第三步 - 深入设计

高级设计简要地涵盖了两个流程:订阅发布和新闻订阅构建。在这里,我们将更深入地讨论这些话题。

订阅发布深度解析

图 11-4 描绘了订阅发布的详细设计。我们已经在高级设计中讨论了大部分的组件,现在我们将聚焦于两个组件:Web 服务器和扩散服务。

image-20230525202628644

Web 服务器

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

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

扩散服务

扩散是将帖子传递给所有朋友的过程。有两种类型的扩散模型:写时扩散(也称为推送模型)和读时扩散(也称为拉取模型)。这两种模型都有利弊。我们将解释他们的工作流程,并探索最佳方式来支持我们的系统。

写时扩散。 在这种方法中,新闻订阅在写入时预先计算。新帖子发布后立即发送到朋友的缓存。

优点:

  • 新闻订阅是实时生成的,可以立即推送给朋友。
  • 因为新闻订阅在写入时就预先计算,所以获取新闻订阅非常快。

缺点:

  • 如果一个用户有很多朋友,获取朋友列表并为他们所有人生成新闻订阅是缓慢且耗时的。这就是所谓的热键问题。
  • 对于不活跃的用户或者很少登录的用户,预先计算新闻订阅会浪费计算资源。

读时扩散。 在读取时生成新闻订阅。这是一个按需模型。用户加载主页时,会拉取最近的帖子。

优点:

  • 对于不活跃的用户或者很少登录的用户,读时扩散效果更好,因为它不会在他们身上浪费计算资源。
  • 数据不会推送给朋友,所以没有热键问题。

缺点:

  • 获取新闻订阅速度慢,因为新闻订阅没有预先计算。

我们采用了一种混合方法,以便从这两种方法中都获取到好处并避免其中的陷阱。由于快速获取新闻订阅很关键,所以我们对大部分用户使用推送模型。对于那些有许多朋友/粉丝的名人或用户,我们让粉丝按需拉取新闻内容,以避免系统过载。一致性哈希是一种有用的技术,可以帮助更均匀地分布请求/数据,从而减轻热键问题。

让我们仔细看一下图 11-5 中展示的扩散服务。

image-20230525202659614

扩散服务的工作方式如下:

  1. 从图数据库获取朋友ID。图数据库适合管理朋友关系和朋友推荐。有兴趣了解更多关于这个概念的读者,可以参考参考资料[2]。
  2. 从用户缓存获取朋友信息。然后系统根据用户设置过滤朋友。例如,如果你屏蔽了某人,即使你们仍然是朋友,她的帖子也不会出现在你的新闻订阅中。帖子可能不显示的另一个原因是,用户可能选择性地与特定朋友分享信息,或者从其他人那里隐藏信息。
  3. 将朋友列表和新帖子ID发送到消息队列。
  4. 扩散工作人员从消息队列获取数据,并将新闻订阅数据存储在新闻订阅缓存中。你可以将新闻订阅缓存看作是一个 ** 映射表。每当发布一个新帖子,它都会被添加到图 11-6 中显示的新闻订阅表中。如果我们在缓存中存储整个用户和帖子对象,内存消耗可能会变得非常大。因此,只存储ID。为了保持内存大小小,我们设置了一个可配置的限制。用户在新闻订阅中浏览成千上万的帖子的机会很小。大多数用户只对最新的内容感兴趣,所以缓存未命中率很低。
  5. 在新闻订阅缓存中存储 **。图 11-6 显示了缓存中的新闻订阅的样子。

image-20230525202713257

新闻订阅获取深度解析

图 11-7 描述了新闻订阅获取的详细设计。

image-20230525202726527

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

  1. 用户发送请求获取她的新闻订阅。请求如下:/v1/me/feed
  2. 负载均衡器将请求重新分配到网络服务器。
  3. 网络服务器调用新闻订阅服务获取新闻订阅。
  4. 新闻订阅服务从新闻订阅缓存获取帖子 ID 列表。
  5. 用户的新闻订阅不仅仅是一份订阅 ID 列表。它包含用户名、个人资料图片、帖子内容、帖子图片等。因此,新闻订阅服务从缓存(用户缓存和帖子缓存)中获取完整的用户和帖子对象,以构建完全填充的新闻订阅。
  6. 完全填充的新闻订阅以 JSON 格式返回给客户端进行渲染。

缓存架构

缓存对于新闻订阅系统至关重要。我们将缓存层分为图 11-8 所示的 5 层。

image-20230525202745402

  • 新闻订阅:存储新闻订阅的 ID。
  • 内容:存储每个帖子的数据。热门内容存储在热缓存中。
  • 社交图:存储用户关系数据。
  • 行动:存储用户是否喜欢一篇帖子,回复一篇帖子,或对一篇帖子进行其他操作的信息。
  • 计数器:存储点赞、回复、关注者、正在关注等的计数器。

步骤4 - 总结

在这一章中,我们设计了一个新闻订阅系统。我们的设计包含两个流程:订阅发布和新闻订阅检索。

就像任何系统设计面试问题一样,没有完美的设计系统的方式。每个公司都有其独特的约束,你必须设计一个符合这些约束的系统。理解你的设计和技术选择的权衡是重要的。如果还有几分钟的时间,你可以谈论可扩展性问题。为避免重复的讨论,下面只列出了高层次的讨论点。

扩展数据库:

  • 垂直扩展 vs 水平扩展
  • SQL vs NoSQL
  • 主从复制
  • 读取副本
  • 一致性模型
  • 数据库分片

其他讨论点:

  • 保持网络层无状态
  • 尽可能地缓存数据
  • 支持多个数据中心
  • 使用消息队列降低组件耦合
  • 监控关键指标。例如,监控高峰时段的 QPS 和用户刷新新闻订阅时的延迟是很有趣的。

恭喜你走到了这里!现在给自己一个鼓励。做得好!

参考资料

[1] 新闻订阅是如何工作的:

https://www.facebook.com/help/327131014036297/

[2] 使用 Neo4j 和 SQL Sever 推荐朋友的朋友:

http://geekswithblogs.net/brendonpage/archive/2015/10/26/friend-of-friend-recommendations-with-neo4j.aspx

你好,我是拾叁,7年开发老司机、互联网两年外企5年。怼得过阿三老美,也被PR comments搞崩溃过。这些年我打过工,创过业,接过私活,也混过upwork。赚过钱也亏过钱。一路过来,给我最深的感受就是不管学什么,一定要不断学习。只要你能坚持下来,就很容易实现弯道超车!所以,不要问我现在干什么是否来得及。如果你还没什么方向,可以先关注我[公众号:更AI (power_ai)],这里会经常分享一些前沿资讯和编程知识,帮你积累弯道超车的资本。

猜你喜欢

转载自blog.csdn.net/smarter_AI/article/details/131798061