Feed 流架构设计 | 青训营笔记

445 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天。

前言

本次青训营的大项目需要我们实现一个极简化的抖音后端。 而类抖音平台的一个极重要的组成部分便是它们的 feed 流分发部分。 本文总结了以下的一些文章,提取出了一些可能的设计思路。

参考文章

概念:Feed 流

Data feed is a mechanism for users to receive updated data from data sources.

-- Data feed - Wikipedia

Feed 这一概念主要指的是把信息从数据源发送给用户的机制。 只要产品里涉及到信息的发布者和接受者,那么架构中就一定会涉及到 feed。 Feed 实现可以偏向简单也可以非常复杂,例如:

  • 最简单的 RSS 中,数据源提供一个信息列表,用户需要自己进行轮询。
  • 最简单的邮件列表系统中,数据源负责将邮件一封一封分别发送到每一位订阅者的邮箱。
  • 微博里用户不应收到其拉黑的用户的信息。
  • 微博里,时间线可以分为两种类型:
    • 按时间排序;
    • 综合各种条件产生的乱序时间线。

总之,根据平台性质、流量、需求的不同,其架构设计可能也会有不同。 一般来说,feed 流可以分为推模式和拉模式。

拉模式

用一条(未经优化的)SQL 语句也许更言简意赅:

SELECT * FROM video JOIN follow
         ON video.author = follow.following
         WHERE follow.follower = 'my_user_id'
         ORDER BY video.updated DESC
         LIMIT 30 OFFSET 0;

用户每请求一次 feed 流,我们都需要进行至少一次复杂的 SQL 请求:

  • 将用户所关注的所有信息发布者所发布的信息汇总起来;
  • 按某种逻辑将这些信息排序(上面的 SQL 简单地按时间排序了);
  • 进行分页。

因为这种机制是在用户请求的时候再从源头拉取信息来组织时间线,这种模式被称为“拉模式”。 上面提到的 RSS 就是一个拉模式的典型例子。

推模式

一些开源项目使用邮件列表来进行交流(如 mail.openjdk.org)。 邮件列表系统是推模式的一个典型例子:

  • 每个人都有一个邮箱;
  • 当某个人将邮件发到邮件列表时,系统会将这封邮件转发到所有订阅了的人手上;
  • 订阅者的邮箱就是自己的 feed。

推/拉模式比较

推模式拉模式
发布耗时正比于粉丝数
请求耗时正比于发布者数及其历史发文数
分页建索引即可必须实时计算
储存空间正比于粉丝数、发文数

拉模式有自己的优势。 但是大多数的平台中,读者请求数都是大大多于作者发布数的, 因而请求时间小的推模式具有极大的优势。 另外,推模式也适于加入个性化内容和选择性推送功能。 而现有的大多数平台架构也都基于推模式。

架构

推模式的架构在数据人看Feed流-架构实践一文已有详细论述。 限于个人水平,这里就不再妄加评论了。

推模式:储存

推模式:实际考量

当然,在实际进行代码编写的时候我们还要考虑一些问题:

  • 当用户 A 关注用户 B 的时候,我们需不需要更新用户 A 的 feed 以加入 B 的内容?
  • 当用户 A 取关用户 B 的时候,我们需不需要更新用户 A 的 feed以删去内容?
  • 如果上面两个问题的答案为“是”,那么有没有可能出现下面的情况?
    • 用户 A 关注 B 时,B 恰好发布了一篇文章,这篇文章有可能永远不会出现在 A 的 feed 里。
    • 用户 A 取关 B 时,B 恰好发布了一篇文章,这篇文章有可能并不会被从 A 的 feed 里删去。

上面的两种情况甚至用户可能也不会太在意,但却是的的确确的逻辑漏洞。 其触发的并发执行情况如下:

A 取关过程B 分发过程
获取 B 的粉丝列表(包含 A)
将 A 从 B 的粉丝列表删去
将 B 的内容从 A 的 feed 删去
将 B 的新内容放入粉丝的 feed 中
现在 A 不是 B 的粉丝了但是 A 的 feed 里仍然有 B 的内容

这种情况在 B 的粉丝数量极大时可能会经常出现,因为此时 B 的新内容的分发可能会需要较多时间, 在这段时间里发生的关注或取关操作便很可能会触发这种漏洞。

同时,因为 B 的分发过程耗时长,所有基于锁的解决方案都可能会有性能问题。 个人的想法是暂存一下发生变化的用户关系并在必要时进行检查。 当然,我们也可以就这么希望着用户永远不会留意到这种情况。

总结

推模式是主流的选择。但推/拉模式的选择只是 feed 流架构设计的开始,考虑了储存、负载均衡、缓存等各种优化手段后的实际架构比“推”“拉”二字会复杂得多。 数据人看Feed流-架构实践一文中推荐了初创公司的一种迭代路径,其架构设计值得参考。