系统设计-Ch11-设计一个动态推送系统

设计一个动态推送系统

在本章中,您被要求设计一个动态推送系统。动态推送系统系统是在主页中间不断更新的动态消息列表。

动态消息包括状态更新、照片、视频、链接、应用活动以及你关注的人、页面和喜欢的群组。常见的问题有:设计Facebook news feeds, Instagram feed, Twitter timeline等等。

Step1:Understand the problem and establish design scope

对于”设计一个信息流系统“这个系统设计话题,我们可以向面试官提出以下问题:

  • Q:这是一个mobile app还是一个web app?

    A:两者都有

  • Q:有哪些重要功能?

    A:用户可以发布动态,并在动态推送页面上看到他朋友(关注的人)的动态。

  • Q: 动态推送的动态是按照时间倒序还是按照评价分数或其他特定顺序排列?

    A:为简单起见,是按照逆时间顺序排序的

  • Q:动态是否可以包含图像或视频,还是只包含文本?

    A:可以包含媒体文件,包括图像和视频

  • Q: APP的日活量是多少?

    A:1000万DAU

Step2:Propose high-level design and get buy-in

设计分为两个流程:Feed Publishing 和 News Feed Building。

  • Feed Publishing:当用户发布动态时,相应的数据会写入缓存和数据库。她朋友的动态推送会有一个动态
  • News Feed Building:动态推送是按照时间逆序聚合朋友们的动态来构建的

Newsfeed APIs

动态推送APIs是客户端和服务器通信的主要方式。这些APIs基于HTTP。这里我们讨论两个最重要的API: feed publishing API(新闻发布)和 news feed retrieval API(动态推送检索)

Feed Publishing

Feed Publishing流程如下:

  • 用户可以在浏览器或手机APP上通过以下API发布内容为"Hello"的动态:

    /v1/me/feeds? content=Hello & auth_token={auth_token}
  • 负载均衡器:将请求按负载均衡策略分配到web服务器上

  • web服务器:将请求重定向到不同的内部服务

  • Post Service:将动态在缓存和数据库中持久化

  • Fanout Service:将新动态推送到朋友的动态推送页面, Newsfeed 数据存储在缓存中以便快速检索

  • Notification Service:发送通知,通知朋友有新的动态可以查看

NewsFeed Building

News Feed Building流程如下:

  • 用户可以在浏览器或手机APP上通过以下API刷新她的动态推送:

    /v1/me/feed
  • 负载均衡器:将请求按负载均衡策略分配到web服务器上

  • web服务器:将请求重定向到动态推送服务NewsFeed Service

  • NewsFeed Service:动态推送服务从缓存中获取新的动态

Step3:Design deep dive

Feed Publishing deep dive

下图概述了Feed Publishing的详细设计:

我们主要详细讨论一下这张图中的Web Servers和Fanout Service两个部分。

Web Servers

除了与客户端通信外,Web服务器还要执行身份验证(Authentication)和速率限制)(Rate Limiting)任务。

其实很简单,为了避免垃圾邮件和API滥用,只有携带相应auth_token的用户才能发帖,并且在一段时间内用户的发帖有数量限制。

Fanout service

Fanout 是向所有朋友发送动态的过程,Fanout有两种实现方式:Fanout on write 和Fanout on read,我们将分别介绍其优劣,并探讨选择最适合我们这个系统的实现方式。


Fanout on write

使用Fanout on write这种方式时,动态推送(News Feed)会在编写时预先计算,新动态发布后会立即发送到朋友的缓存中。

优点:

  • 动态推送是实时生成的,可以立即推送给朋友
  • 获取动态推送很快,因为动态推送是在写入时预先计算的

缺点:

  • 如果一个用户有很多朋友,那么获取朋友列表并为所有朋友生成动态推送既缓慢又耗时,这被称为热键问题(hotkey problem)
  • 对于不活跃的用户,预计算动态推送会浪费计算资源

Fanout on read

使用Fanout on read这种方式时,动态推送是在阅读期间生成的。这是一种按需模式。当用户加载其主页时,会拉取最新的未读动态。

优点:

  • 对于不活跃的用户,这种Fanout效果更好,因为不会浪费计算资源
  • 数据不会主动推送给朋友,因此不存在热键问题

缺点:

  • 获取动态推送很慢,因为动态推送是用户浏览时实时拉取计算的

我们采用一种混合方法来综合这两种方法的优点:

由于快速获取动态推送至关重要,我们为大多数用户使用Fanout on write模式。对于有很多朋友/关注者的用户(比如说:明星),我们允许关注者按需拉取动态推送内容,以避免系统过载。一致性哈希是一种有效的技术,可以缓解热键问题,因为它有助于更均匀地分配 请求/数据。

让我们单独把Fanout Service拿出来看一下:

Fanout Service工作原理如下:

  1. 从Graph Database中获取好友ID

    Graph Database使用图这种数据结构来构建数据库,适用于管理朋友关系和朋友推荐

  2. 从用户缓存中获取好友信息,然后系统会根据用户设置过滤掉好友。(例如设置屏蔽某个好友或者此动态不可见)

  3. 将好友列表和新帖子ID发送到消息队列Message Queue

  4. Fanout worker从消息队列中获取数据,并将动态数据存储在缓存(News Feed Cache)中

    你可以将News Feed Cache视为<post_id, user_id>的映射表,如下图所示:

    每当用户发布新的动态时,这条动态都会被附加到这张动态推送表中

News Feed Buliding deep dive

下图概述了News Feed Publishing的详细设计:

其中媒体内容(图像、视频等)存储在CDN中,以便快速检索。

让我们看看客户端如何刷新动态:

  • 用户可以在浏览器或手机APP上通过以下API刷新她的动态推送:

    v1/me/feed
  • 负载均衡器:将请求按负载均衡策略分配到web服务器上

  • web服务器:将请求重定向到动态推送服务News Feed Service,同时包含身份验证和速率限制功能

  • News Feed Service:从News Feed Cache中获取post ID列表<post_id, user_id>

  • 一个用户的动态不仅仅包含Post ID列表<post_id, user_id>,还包含用户名,个人资料图片,动态的音视频内容等。因此需要从User Cache中获取完整的用户信息,从Post Cache中获取完整的动态内容信息,从而构建完整的动态推送

  • 完整的动态推送以JSON格式返回给客户端进行渲染

Cache Aerchitecture

缓存对于动态推送系统来说无疑非常重要,我们将缓存分为5层,如下图所示:

  • News Feed Cache:前面也提到过,它存储<post_id, user_id>映射表
  • Content Cache:它存储动态内容数据,热门内容存储在hot cache
  • Social Graph Cache:它存储用户关系数据
  • Action Cache:它存储用户是否喜欢动态,回复动态或者其它操作信息
  • Counters Cache:它存储点赞数量、回复数量、关注人数等计数器

Step4:Warm up

在本章中,我们设计了一个动态推送系统,我们的设计主要包括两个流程,动态发布和动态推送检索。

如果还有几分钟时间,你可以和面试官探讨以下问题:

数据库扩展

  • 垂直缩放和水平缩放
  • SQL与NoSQL
  • 主从复制
  • 一致性模型
  • 数据库分片

其它要点

  • 保持web层无状态
  • 尽可能地缓存更多的数据
  • 支持多个数据中心
  • 监控关键数据,如高峰时间段的QPS等

PS:感觉都是一些第一章第二章谈到的通用问题,意义不大。

主要是浏览一下动态发布和动态推送检索的流程设计,有个印象即可;另外就是还有一个Graph Database,Neo4j,是专门用来存储人际关系的,学到了!!