Reddit 的架构:进化之旅

构建企业级、弹性的 B2B 身份验证是开发人员如今面临的更复杂的任务之一。 如今,即使是规模较小的初创公司也需要 SSO 等安全功能,而这些功能曾经是财富 500 强企业的权限。

WorkOS 的最新指南涵盖了 B2B 应用程序的现代用户管理的要点 – 从 101 个主题到更高级的概念,其中包括:

→ SSO、MFA 和会话

→ 机器人策略、组织身份验证策略和 UI 注意事项

→ 身份链接、电子邮件验证和即时配置

该资源还提供了一种更简单的替代方案来支持用户管理。

阅读更多

Reddit 成立于 2005 年,其愿景是成为“互联网头版”。

多年来,它已发展成为全球最受欢迎的社交网络之一,围绕其成员的热情和兴趣培育了数以万计的社区。 Reddit 每月拥有超过 10 亿用户,人们可以在 Reddit 上参与各种主题的对话。

以下一些有趣的数字表明了 Reddit 令人难以置信的受欢迎程度:

  • Reddit 每月有 12 亿独立访客,将其变成了一个虚拟的城镇广场。

  • 自 2018 年以来,Reddit 的月活跃用户数激增了 366%,这表明了对在线社区的需求。

  • 仅在 2023 年,Reddit 服务器上就出现了惊人的 4.69 亿条帖子,产生了 28.4 亿条评论和互动。

  • Reddit 是 2023 年全球访问量排名第 18 的网站,收入达 8.04 亿美元。

从统计数据来看,他们最近的 IPO 取得巨大成功也就不足为奇了,Reddit 的估值达到了 64 亿美元左右。

虽然财务上的成功可能要归功于领导团队,但如果没有帮助 Reddit 获得如此受欢迎的令人着迷的架构演变之旅,这是不可能的。

在这篇文章中,我们将回顾这段旅程,并探讨改变 Reddit 的一些关键架构步骤。

Reddit 最初是用 Lisp 编写的,但在 2005 年 12 月用 Python 重写了。

Lisp 很棒,但当时的主要问题是缺乏广泛使用和测试的库。 对于任何任务来说,很少有超过一个库可供选择,并且这些库没有正确记录。

Steve Huffman(Reddit 创始人之一)在他的博客中表达了这个问题:

“由于我们主要是站在别人的肩膀上来建设网站,这让事情变得有点困难。 只是没有那么多的肩膀可以支撑。”

当谈到Python时,他们最初使用了由Swartz(Reddit的另一位联合创始人)开发的名为web.py的Web框架。 2009 年晚些时候,Reddit 开始使用 Pylons 作为其 Web 框架。

下图展示了 Reddit 高层架构的核心组件。

虽然 Reddit 有许多移动部件,而且多年来事物也不断发展,但此图代表了支持 Reddit 的整体脚手架。

主要组成部分如下:

  1. 内容交付网络:Reddit 使用 Fastly 的 CDN 作为应用程序的前端。 CDN 在边缘处理大量决策逻辑,以确定如何根据域和路径路由特定请求。

  2. 前端应用程序:Reddit 于 2009 年初开始使用 jQuery。后来,他们还开始使用 Typescript 重新设计 UI,并转向基于 Node.js 的框架以拥抱现代 Web 开发方法。

  3. R2 巨石:中间是一个名为 r2 的巨大盒子。 这是使用 Python 构建的原始整体应用程序,由搜索等功能以及事物和列表等实体组成。 我们将在下一节中更详细地了解 R2 的架构。

从基础设施的角度来看,Reddit 于 2009 年退役了最后一台物理服务器,并将整个网站迁移到 AWS。 他们是 S3 的早期采用者之一,并使用它来托管缩略图和存储日志已有相当长一段时间了。

然而,在 2008 年,他们决定将批处理迁移到 AWS EC2,以释放更多机器作为应用程序服务器。 该系统运行良好,并于 2009 年完全迁移到 EC2。

前面提到,r2 是 Reddit 的核心。

它是一个巨大的单体应用程序,有自己的内部架构,如下所示:

出于可扩展性的原因,相同的应用程序代码被部署并在多个服务器上运行。

负载均衡器位于前面,执行将请求路由到适当的服务器池的任务。 这使得路由不同的请求路径成为可能,例如评论、首页或用户个人资料。

用户投票或提交链接等昂贵的操作通过 Rabbit MQ 推迟到异步作业队列。 消息由应用程序服务器放置在队列中,并由作业处理器处理。

从数据存储的角度来看,Reddit 的核心数据模型依赖于 Postgres。 为了减少数据库的负载,他们将 memcache 集群放在 Postgres 前面。 此外,他们在新功能中大量使用 Cassandra,主要是因为它的弹性和可用性特性。

如果您不是付费订阅者,这就是您错过的内容。

  1. CI/CD 速成班

  2. IPv4 寻址速成班

  3. Netflix 扩展简史

  4. 15 个改变世界的开源项目

  5. 导致你丢掉工作的三大简历错误

要接收所有完整文章并支持 ByteByteGo,请考虑订阅:

随着 Reddit 越来越受欢迎,其用户群也猛增。 为了保持用户的参与度,Reddit 添加了许多新功能。 此外,应用程序的规模及其复杂性也有所增加。

这些变化需要在多个领域改进设计。 虽然设计和架构是一个不断变化的过程,每天都在不断发生微小的变化,但在几个关键领域已经取得了具体的发展。

让我们更详细地了解它们,以了解 Reddit 在架构方面所采取的方向。

Reddit 于 2017 年开始了 GraphQL 之旅。四年内,单体应用程序的客户完全采用了 GraphQL。

GraphQL 是一种 API 规范,允许客户端仅请求他们想要的数据。 这使得它成为多客户端系统的绝佳选择,因为每个客户端的数据需求略有不同。

2021 年初,他们还开始转向 GraphQL Federation,有几个主要目标:

GraphQL Federation 是一种将多个较小的 GraphQL API(也称为子图)组合成单个大型 GraphQL API(称为超级图)的方法。 超级图充当客户端应用程序发送查询和接收数据的中心点。

当客户端向超级图发送查询时,超级图会找出哪些子图具有回答该查询所需的数据。 它将查询的相关部分路由到这些子图,收集响应,并将组合响应发送回客户端。

2022 年,Reddit 的 GraphQL 团队为 Reddit 核心实体(例如 Subreddits 和 Comments)添加了几个新的 Go 子图。 这些子图接管了整个模式的现有部分的所有权。

在过渡阶段,Python 整体和新的 Go 子图协同工作来完成查询。 随着越来越多的功能被提取到各个 Go 子图,单体架构最终可能会被淘汰。

下图显示了这种逐渐过渡。

Reddit 的一项主要要求是逐步处理从单体到新 Go 子图的功能迁移。

他们希望逐渐增加流量以评估错误率和延迟,同时能够在出现任何问题时切换回整体架构。

不幸的是,GraphQL Federation 规范没有提供支持这种流量逐步迁移的方法。 因此,Reddit 采用了蓝/绿子图部署,如下所示:

在这种方法中,Python 整体和 Go 子图共享模式的所有权。 负载均衡器位于网关和子图之间,用于根据配置将流量发送到新子图或整体。

通过这种设置,他们还可以控制整体或新子图处理的流量百分比,从而在迁移过程中提高 Reddit 的稳定性。

截至上次更新,迁移仍在进行中,对 Reddit 体验的影响最小。

在早期阶段,Reddit 支持使用主数据库创建的 WAL 段对其数据库进行数据复制。

WAL 或预写日志是一个文件,用于记录提交之前对数据库所做的所有更改。 它确保如果写入操作期间发生故障,可以从日志中恢复更改。

为了防止这种复制使主数据库陷入困境,Reddit 使用一种特殊工具将 PostgreSQL WAL 文件持续存档到 S3,副本可以从那里读取数据。

然而,这种方法存在一些问题:

  • 由于每日快照是在晚上运行的,因此白天的数据存在不一致的情况。

  • 对数据库的频繁架构更改会导致数据库快照和复制出现问题。

  • 主数据库和副本数据库在 EC2 实例上运行,使得复制过程变得脆弱。 存在多个故障点,例如 S3 备份失败或主节点出现故障。

为了使数据复制更加可靠,Reddit 决定使用 Debezium 和 Kafka Connect 的流式变更数据捕获 (CDC) 解决方案。

Debezium 是一个开源项目,为 CDC 提供低延迟数据流平台。

每当在 Postgres 中添加、删除或修改一行时,Debezium 都会侦听这些更改并将它们写入 Kafka 主题。 下游连接器从 Kafka 主题中读取数据,并使用更改更新目标表。

下图显示了这个过程。

与 Debezium 一起迁移到 CDC 对于 Reddit 来说是一个重大举措,因为他们现在可以将数据实时复制到多个目标系统。

此外,整个过程可以由轻量级 Debezium Pod 来管理,而不是庞大的 EC2 实例。

Reddit 托管了数十亿条帖子,其中包含各种媒体内容,例如图像、视频、GIF 和嵌入式第三方媒体。

多年来,用户上传媒体内容的速度不断加快。 因此,媒体元数据对于增强这些资产的可搜索性和组织变得至关重要。

Reddit 管理媒体元数据的旧方法面临多种挑战:

  • 数据分布并分散在多个系统中。

  • 不同资产类型的存储格式不一致,查询模式也不同。 在某些情况下,他们甚至查询 S3 存储桶以获取元数据信息,这在 Reddit 规模上效率极低。

  • 没有适当的机制来审核更改、分析内容和对元数据进行分类。

为了克服这些挑战,Reddit 构建了一个全新的媒体元数据存储,并提出了一些高级要求:

  • 将不同系统中的所有现有媒体元数据移至同一屋顶下。

  • 支持每秒100K读请求规模的数据检索,延迟小于50ms。

  • 支持数据创建和更新。

数据存储的选择是在 Postgres 和 Cassandra 之间。 Reddit 最终选择了 AWS Aurora Postgres,因为在 Cassandra 中进行临时查询调试存在挑战,以及某些数据未非规范化和不可搜索的潜在风险。

下图显示了 Reddit 媒体元数据存储系统的简化概述。

正如您所看到的,只有一个与数据库连接的简单服务,通过 API 处理读取和写入。

尽管设计并不复杂,但挑战在于将来自不同来源的数TB数据传输到新数据库,同时确保系统继续正确运行。

迁移过程包含多个步骤:

  • 启用从媒体元数据客户端对元数据 API 的双重写入。

  • 将旧数据库中的数据回填到新的元数据存储中。

  • 启用从服务客户端对媒体元数据的双重读取。

  • 监视每个读取请求的数据比较并修复任何数据问题。

  • 增加新元数据存储的读取流量。

请查看下图,该图更详细地显示了此设置。

迁移成功后,Reddit 对媒体元数据存储采取了一些扩展策略。

  1. 使用基于范围的分区进行表分区。

  2. 服务从 Postgres 中的非规范化 JSONB 字段读取。

最终,他们在 p50 时实现了 2.6 毫秒、在 p90 时为 4.7 毫秒、在 p99 时为 17 毫秒的令人印象深刻的读取延迟。 此外,数据存储通常比以前的数据系统更可用,速度也快了 50%。

在媒体领域,Reddit 每天还提供数十亿张图片。

用户上传其帖子、评论和个人资料的图像。 由于这些图像在不同类型的设备上使用,因此它们需要以多种分辨率和格式提供。 因此,Reddit 会针对不同的用例(例如帖子预览、缩略图等)转换这些图像。

自 2015 年以来,Reddit 一直依赖第三方供应商进行即时图像优化。 图像处理不是他们的核心能力,因此,这种方法多年来对他们很有帮助。

然而,随着用户群和流量的不断增加,他们决定将此功能移至内部,以管理成本并控制端到端用户体验。

下图显示了图像优化设置的高级架构。

他们构建了两个用于转换图像的后端服务:

  • Gif2Vid 服务可以即时调整 GIF 大小并将其转码为 MP4。 尽管用户喜欢 GIF 格式,但由于其文件大小较大且计算资源需求较高,因此对于动画资源而言,它是一种低效的选择。

  • 图像优化器服务处理所有其他图像类型。 它使用 govips,它是 libvips 图像处理库的包装器。 该服务处理大部分缓存未命中流量,并处理图像转换,例如模糊、裁剪、调整大小、覆盖图像和格式转换。

总体而言,在内部进行图像优化非常成功:

  • Gif2Vid 转换成本仅降低至原始成本的 0.9%。

  • 编码动画 GIF 的 p99 缓存未命中延迟从 20 秒降至 4 秒。

  • 静态图像的字节数减少了大约 20%。

Reddit 的一项关键功能是审核违反平台政策的内容。 对于数十亿将 Reddit 视为社区的用户来说,这对于确保 Reddit 网站的安全至关重要。

2016年,他们开发了一个名为Rule-Executor-V1 (REV1)的规则引擎,以实时遏制网站上违反政策的内容。 REV1 使安全团队能够创建规则,根据用户发布新内容或留下评论等活动自动采取行动。

作为参考,规则只是在特定配置事件上触发的 Lua 脚本。 实际上,这可以是如下所示的一段简单代码:

在此示例中,该规则检查帖子的文本正文是否与字符串“some bad text”匹配。 如果是,它将通过将操作发布到输出 Kafka 主题来对用户执行异步操作。

然而,REV1 需要一些重大改进:

  • 它在原始 EC2 实例的遗留基础设施上运行。 这与 Reddit 上运行在 Kubernetes 上的所有现代服务并不相符。

  • 每个规则在 REV1 节点中作为单独的进程运行,并且随着更多规则的启动需要垂直扩展。 超过某一点,垂直扩展变得昂贵且不可持续。

  • REV1 使用已弃用的 Python 2.7。

  • 规则不受版本控制,并且很难理解更改的历史。

  • 缺乏以沙盒方式测试规则的暂存环境。

2021 年,Reddit 内的安全工程团队开发了一种名为 Snooron 的新流媒体基础设施。 它构建在 Flink Stateful Functions 之上,以实现 REV1 架构的现代化。 新系统被称为 REV2。

下图显示了 REV1 和 REV2。

REV1 和 REV2 之间的一些主要区别如下:

  • 在 REV1 中,所有规则配置都是通过 Web 界面完成的。 对于 REV2,配置主要通过代码进行。 不过,有一些 UI 实用程序可以使该过程变得更简单。

  • 在 REV1 中,他们使用 Zookeeper 作为规则存储。 在 REV2 中,规则存储在 Github 中以实现更好的版本控制,并且还保存到 S3 中以进行备份和定期更新。

  • 在 REV1 中,每个规则都有自己的进程,触发时会加载最新的代码。 但是,当同时运行太多规则时,这会导致性能问题。 REV2 采用不同的方法,使用 Flink Stateful Functions 来处理事件流,并使用单独的 Baseplate 应用程序来执行 Lua 代码。

  • 在 REV1 中,规则触发的操作由主 R2 应用程序处理。 然而,REV2 的工作方式有所不同。 当规则被触发时,它会向多个操作主题发送结构化的 Protobuf 操作。 使用 Flink Statefun 构建的名为 Safety Actioning Worker 的新应用程序接收并处理这些指令以执行操作。

提要是社交媒体和社区网站的支柱。

每天有数百万人使用 Reddit 的提要,它是网站整体可用性的关键组成部分。 开发 feed 架构时有一些关键目标:

  • 该架构应支持高开发速度并支持可扩展性。 由于许多团队都与提要集成,因此他们需要有能力快速理解、构建和测试它们。

  • TTI(交互时间)和滚动性能应该令人满意,因为它们对于用户参与度和整体 Reddit 体验至关重要。

  • Feed 在不同平台(例如 iOS、Android 和网站)之间应该保持一致。

为了支持这些目标,Reddit 构建了一个全新的服务器驱动的提要平台。 Feed 的后端架构发生了一些重大变化。

早些时候,每个帖子都由一个 Post 对象表示,该对象包含帖子可能具有的所有信息。 这就像通过电线发送厨房水槽一样,随着新的柱类型的出现,柱对象随着时间的推移变得相当大。

这对客户来说也是一种负担。 每个客户端应用程序都包含一堆繁琐的逻辑来确定 UI 上应显示的内容。 大多数时候,这种逻辑在不同平台上是不同步的。

随着架构的变化,他们不再使用大对象,而是仅发送客户端将呈现的确切 UI 元素的描述。 后端控制元素的类型及其顺序。 这种方法也称为服务器驱动的 UI。

例如,帖子单元由包含 Cell 对象数组的通用 Group 对象表示。 下图显示了公告项和摘要中第一个帖子的响应结构的变化。

最初,Reddit 采用 Thrift 来构建其微服务。

Thrift 使开发人员能够定义一个通用接口(或 API),使不同的服务能够相互通信,即使它们是用不同的编程语言编写的。 Thrift 采用独立于语言的接口并为每种特定语言生成代码绑定。

这样,开发人员可以使用对其编程语言来说看起来很自然的语法从代码中进行 API 调用,而不必担心底层的跨语言通信细节。

多年来,Reddit 的工程团队构建了数百个基于 Thrift 的微服务,尽管它为他们提供了很好的服务,但 Reddit 不断增长的需求使得继续使用 Thrift 的成本高昂。

gRPC 于 2016 年出现,并在云原生生态系统中获得了广泛采用。

gRPC 的一些优点如下:

  • 它为 HTTP2 作为传输协议提供本机支持

  • Istio 和 Linkerd 等多种服务网格技术原生支持 gRPC

  • 公共云提供商还支持 gRPC 原生负载均衡器

虽然 gRPC 有很多好处,但转换的成本却不容小觑。 然而,这是一次性成本,而在 Thrift 中构建同等功能将是一项持续的维护活动。

Reddit 决定过渡到 gRPC。 下图显示了他们用于启动迁移过程的设计:

主要组件是过渡垫片。 它的工作是充当新 gRPC 协议和现有基于 Thrift 的服务之间的桥梁。

当 gRPC 请求传入时,填充程序会将其转换为等效的 Thrift 消息格式,并将其传递到现有代码,就像本机 Thrift 一样。 当服务返回响应对象时,填充程序会将其转换回 gRPC 格式。

该设计分为三个主要部分:

  • 接口定义语言 (IDL) 转换器,用于将 Thrift 服务定义转换为相应的 gRPC 接口。 该组件还负责酌情调整框架习惯用法和差异。

  • 代码生成的 gRPC 服务程序,用于处理 Thrift 和 gRPC 之间传入和传出消息的消息转换。

  • 服务的可插入模块,支持 Thrift 和 gRPC。

这种设计允许 Reddit 通过重用现有的基于 Thrift 的服务代码来逐步过渡到 gRPC,同时控制迁移所需的成本和工作量。

Reddit 的架构之旅一直是不断演变的旅程,多年来受到其快速增长和不断变化的需求的推动。 最初是一个单一的 Lisp 应用程序,后来用 Python 进行了重写,但随着 Reddit 的流行度爆炸式增长,这种单一的方法无法跟上步伐。

该公司雄心勃勃地向基于服务的架构转型。 他们面临的每个新功能和问题都促使用户保护、媒体元数据管理、通信渠道、数据复制、API 管理等各个领域的整体设计发生变化。

在这篇文章中,我们试图根据现有信息来捕捉 Reddit 架构从早期到最新变化的演变。

参考:

将您的产品展示在超过 500,000 名技术专业人士面前。

我们的时事通讯将您的产品和服务直接呈现在重要受众面前 – 数十万工程领导者和高级工程师 – 他们对重大技术决策和大宗采购具有影响力。

空间很快就满了 – 立即预订

广告位通常会提前约 4 周售完。 为了确保您的广告吸引到这些有影响力的受众,请立即通过电子邮件预订您的空间 [email protected]

Leave a Reply

Your email address will not be published. Required fields are marked *

近期新闻​

编辑精选​