Skip to content

搞英语 → 看世界

翻译英文优质信息和名人推特

Menu
  • 首页
  • 作者列表
  • 独立博客
  • 专业媒体
  • 名人推特
  • 邮件列表
  • 关于本站
Menu

Netflix Tudum 如何利用 CQRS 支持 2000 万用户

Posted on 2025-09-10

构建与 Speakeasy 的 Gram 配合使用的 MCP 服务器(赞助)

MCP 服务器包含过多工具,缺乏关键上下文,并且只是简单的 API 镜像,这会让 AI 代理感到困惑。如果没有开发,精心设计的 API 可能会让代理感到头疼。

Gram 解决了这个问题。它是一个开源平台,您可以在其中管理工具:添加上下文、设计多步骤工具,并在几分钟内部署您的 MCP 服务器。

将您的 API 转变为代理就绪基础设施,该基础设施可通过 OAuth 2.1 支持、集中管理和托管基础设施进行扩展。

开始构建您的 MCP 服务器 →


免责声明:本文中的详细信息源自 Netflix 工程团队在线分享的官方文档。所有技术细节的版权均归 Netflix 工程团队所有。原文和来源的链接位于文章末尾的参考文献部分。我们已尽力分析这些细节并提供意见。如果您发现任何错误或遗漏,请留言,我们将尽力修正。

当 Netflix 推出 Tudum 作为其幕后故事、粉丝访谈和互动体验的官方平台时,工程挑战显而易见:以高速向数百万观众提供新鲜、格式丰富的内容,同时让编辑者能够无缝地实时预览更新。

最初的架构遵循经典的 CQRS(命令查询职责分离)模式,将编辑工具的“写入路径”与访客的“读取路径”分离。Kafka 连接了这些路径,将读取优化后的数据推送到后端服务进行页面构建。

该方法具有可扩展性和可靠性,但也存在一些缺点。

随着 Tudum 的发展,编辑们注意到,从保存更新到预览上线之间存在着令人沮丧的延迟。罪魁祸首是一系列连续的流程和缓存刷新周期,虽然这些流程对于生产访客来说很合适,但却拖慢了创作工作流程。

为了解决这个问题,Netflix 工程师用 RAW Hollow(一种直接嵌入在应用程序中的压缩、分布式内存对象存储)替换了读取路径的外部键值存储和每个请求的 I/O。

最终结果是近乎即时的编辑预览、更精简的基础设施,以及大幅缩短了终端用户的页面构建时间。在本文中,我们将探讨这一设计决策的演变过程,以及 Netflix 是如何实现它的。

早期设计

Netflix 的 Tudum 平台必须支持两种根本不同的工作流程:

  • 写入路径:这是内容编辑者在内容管理系统 (CMS) 中创建和更新丰富的媒体密集型故事的地方。

  • 阅读路径:这是数百万全球访问者以针对快速呈现和传递而优化的格式阅读这些故事的地方。

为了保持这些工作流程独立并允许每个工作流程根据其需求进行扩展,Netflix 采用了 CQRS(命令查询职责分离)架构。

请参阅下图了解 CQRS 的总体概述。

写入存储包含原始编辑数据(带有 ID、元数据和引用的内部 CMS 对象),而读取存储包含相同数据的完全“渲染就绪”版本,例如已解析的电影标题而不是 ID、CDN 就绪的图像 URL 而不是内部资产引用,以及预先计算的布局元素。

如上所述,Kafka 充当了这两条路径之间的桥梁。当编辑进行更改时,CMS 会向 Tudum 的摄取层发送事件。该摄取管道执行以下步骤:

  • 从 CMS 中提取内容。

  • 应用模板和业务规则来确保格式的一致性。

  • 验证了必填字段和约束的数据。

  • 将占位符转换为可用于生产的资产(例如,电影标题查找、CDN URL 解析)。

处理后的内容发布到 Kafka 主题。

数据服务消费者订阅了此主题,读取每个新增或更新的页面元素。它将这些数据写入由 Cassandra 支持的读取存储,该存储经过结构化处理,以便快速检索。最后,API 层将这些读取优化的实体暴露给下游消费者,例如页面构建服务(负责组装完整页面以供渲染)、个性化服务和其他内部工具。

参见下图:

这种事件驱动的设计确保了编辑更改最终会出现在 Tudum 网站上,而不会影响写入端的性能,同时还允许 Netflix 独立扩展读取和写入路径。

最终一致性的痛苦

虽然 CQRS 与 Kafka 的设计非常强大且可扩展,但它引入了工作流程瓶颈,随着 Tudum 编辑产出的增长,这一问题变得越来越明显。

每次编辑者在CMS中进行更改时,该更改都必须经过一个漫长的流程,才能在预览环境或正式网站上线。以下是其中涉及的各个步骤的简要介绍:

  • 保存在CMS中:更新的内容存储在写入数据库中。

  • Webhook 到 Tudum 摄取: CMS 将更改通知 Tudum 的摄取层。

  • 重新读取和处理:摄取获取相关部分、应用模板、验证数据并执行资产解析。

  • 发布到Kafka:将处理后的内容发送到指定的Kafka Topic。

  • 消费和存储:数据服务消费者接收消息,将其作为读取优化记录写入 Cassandra。

  • 缓存刷新:位于 Cassandra 前面的页面数据服务维护着一个近缓存,必须刷新该近缓存才能显示新内容。

这个近缓存是造成延迟的一个关键因素。从技术上讲,近缓存是一个小型的、基于每个实例的内存层,位于读取存储的前端。但是,它并非针对每次更新立即刷新,而是按照每个键的计划刷新策略运行。每个键都有一个计时器。当计时器触发时,实例会从后备存储中刷新该键。虽然这种方法是为了提高生产流量效率而设计的,但它意味着新的编辑通常要等到下一个计划的刷新周期才能显示。

随着内容量和页面元素数量的增加,刷新周期也随之延长。一个页面由多个片段组成,每个片段都有各自的键和计时器。它们不会同时刷新。这意味着页面元素越多,刷新完成就越错开,从而导致预览状态不一致。换句话说,有些元素更新了,但其他元素仍然保持原样。

结果是,即使系统已经处理并存储了更新,编辑者有时也必须等待几分钟才能在预览中看到他们的更改。

对于 Tudum 这样的平台来说,发布与新版本和事件相关的故事的时间至关重要,这种延迟扰乱了编辑流程并复杂化了作家、编辑和设计师之间的协作。

解决方案:RAW Hollow

为了消除 Tudum 读取路径中的瓶颈,Netflix 工程师转向了 RAW Hollow:一种压缩的、分布式的内存对象存储,专为数据集规模小到中等、不频繁更改且必须以极低延迟提供服务的场景而设计。

与之前的设置不同,读取服务从外部 Cassandra 支持的键值存储(通过网络调用、缓存层和刷新周期)获取数据,而 RAW Hollow 会将整个数据集直接加载到每个需要它的应用程序实例的内存中。这意味着所有查找都在进程内进行,从而避免了旧方法中 I/O 和缓存失效的复杂性。

在 Tudum 环境下,RAW Hollow 的主要特征如下:

  • 分布式和共置:每个服务实例都将完整数据集保存在内存中。更新会进行传播,因此所有实例都能保持同步,无需按需查询外部存储。

  • 压缩以实现可扩展性:数据以压缩的二进制形式存储,显著减少了内存占用。以 Tudum 为例,三年的未水化数据大小约为 130 MB,约为 Apache Iceberg 中相同数据的四分之一。

  • 按请求进行一致性控制: RAW Hollow 默认采用最终一致性以实现高可用性,但服务可以根据每个请求选择启用强读后写一致性。这对于编辑器预览流程尤其有用,可确保刚发布的更新立即可见,而无需等待标准同步间隔。

  • 专为内存计算而构建:由于数据集始终位于 RAM 中,因此页面构建、搜索和个性化等服务可以在 O(1) 时间内检索数据,从而消除网络往返。

对于 Tudum 来说,采用 RAW Hollow 意味着从读取路径中移除页面数据服务、其近缓存、外部键值存储,甚至 Kafka。取而代之的是,Hollow 客户端直接嵌入到每个需要内容的微服务中。这减少了连续操作的数量,缩短了编辑者的反馈循环,并通过移除多个移动部件简化了架构。

结果是一个巨大的转变:系统不再是“存储、获取、缓存、刷新”,而是“加载一次到内存中,立即传播变化”。

全新 TUDUM 设计

采用 RAW Hollow 后,Netflix 重建了 Tudum 的读取路径,以删除那些减慢编辑预览速度并增加不必要复杂性的层。

新的设计仍然遵循 CQRS 原则(将编辑内容创建与面向访问者的内容分开),但数据在读取端的移动方式现在得到了彻底的简化。

参见下图:

以下是架构上的变化:

  • 不再使用页面数据服务和近缓存:基于键值存储的旧式外观(该外观管理其自身的预定缓存刷新周期)已被彻底移除。服务不再等待缓存更新:它们所需的数据已存储在内存中。

  • 读取路径无需外部键值存储:页面渲染或个性化设置期间不再查询 Cassandra(及其所需的 I/O)。取而代之的是,整个读取优化数据集通过 RAW Hollow 存储在每个服务实例的 RAM 中。

  • 读取路径中不再使用 Kafka:虽然 Kafka 在其他方面仍有用例,但读取端不再依赖 Kafka 来传播更新以服务实时流量。RAW Hollow 内部处理数据分发和同步。

  • 每个微服务中都嵌入了 Hollow 客户端:页面构建、搜索和个性化等服务现在都运行各自的嵌入式 Hollow 客户端。这使得它们能够直接以 O(1) 时间访问最新的读取优化内容,而无需网络调用。

新流程的工作原理如下:

  • 当内容在写入路径中更新时,它会被处理为读取优化格式

  • RAW Hollow 会将该更新分发给所有服务实例中的 Hollow 客户端。这被称为 Hollow 状态,它是每个服务进程本地保存的内存数据集。

  • 由于每个实例在内存中都有完整的数据集,因此任何请求(无论是来自编辑器预览还是实时用户)都会立即得到满足,而无需缓存检查或数据存储区查询。

  • 对于编辑预览,服务可以请求强大的读写一致性,确保立即显示最新的更新。

此次架构重构将 Tudum 的读取路径从多跳网络绑定管道迁移到了内存本地查找模型。本质上,Netflix 继承了 CQRS 的可扩展性和分离性,但剥离了读取路径中 I/O 密集型管道,将其替换为内存优先的嵌入式数据模型。

结论

从基于缓存读取路径的 Kafka 和 Cassandra 迁移到 RAW Hollow 内存模型,为 Tudum 带来了立竿见影且可衡量的改进。一些主要优势如下:

  • 大幅降低延迟:在受控测试中(剔除 TLS、身份验证、WAF 和日志记录开销),一旦所有读取路径服务都使用 Hollow 的内存状态,主页构建时间将从大约 1.4 秒缩短至约 0.4 秒。这一速度提升将直接影响编辑预览和实时访客请求。

  • 近乎即时的编辑预览:编辑者现在可以在几秒钟内看到更新,而无需等待几分钟的缓存刷新周期。预览请求的强大读写一致性确保更改在 CMS 中保存后立即可见。

  • 内存占用小:压缩后,整个读取数据集可以轻松加载到 RAM 中。三年的未水化内容大小约为 130 MB,约为之前 Apache Iceberg 格式的 25%。这使得完整数据集可以与每个服务实例共存,而无需进行昂贵的扩展。

  • 操作简单:从读取路径中删除 Kafka、外部键值存储和近缓存层,减少了移动部件和故障点,同时消除了缓存失效的问题。

Netflix 对 Tudum 读取路径的重新架构表明,重新思考数据访问模式可以带来性能、简洁性和开发人员体验方面的巨大提升。

通过将 CQRS 的可扩展性与 RAW Hollow 等内存压缩对象存储的速度相结合,他们创建了一个既能满足编辑灵活性又能满足最终用户响应能力的系统。

这里的经验教训具有广泛的适用性:

  • 最小化延迟关键流程中的顺序操作。

  • 使经常访问的数据集尽可能靠近应用程序。

  • 选择性地使用一致性控制来平衡新鲜度和可用性。

参考:

  • Netflix Tudum 架构:从使用 Kafka 的 CQRS 到使用 RAW Hollow 的 CQRS

  • 命令查询责任分离

  • CQRS 模式


ByteByteGo 技术面试准备工具包

推出一体化面试准备课程。我们将在ByteByteGo 网站上提供所有书籍。

包含的内容:

  • 系统设计面试

  • 编码面试模式

  • 面向对象设计面试

  • 如何写好简历

  • 行为面试(即将推出)

  • 机器学习系统设计面试

  • 生成式人工智能系统设计面试

  • 移动系统设计面试

  • 更多精彩即将呈现

首发优惠:50% 折扣


赞助我们

将您的产品展示给超过 1,000,000 名技术专业人士。

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

房间很快订满 – 立即预订

广告位通常提前4周左右售罄。为了确保您的广告能够触达这些极具影响力的受众,请立即发送电子邮件至[email protected] 预订您的广告位。

原文: https://blog.bytebytego.com/p/how-netflix-tudum-supports-20-million

本站文章系自动翻译,站长会周期检查,如果有不当内容,请点此留言,非常感谢。
  • Abhinav
  • Abigail Pain
  • Adam Fortuna
  • Alberto Gallego
  • Alex Wlchan
  • Anil Dash
  • Answer.AI
  • Arne Bahlo
  • Ben Carlson
  • Ben Kuhn
  • Bert Hubert
  • Big Technology
  • Bits about Money
  • Brandon Skerritt
  • Brian Krebs
  • ByteByteGo
  • Chip Huyen
  • Chips and Cheese
  • Christopher Butler
  • Colin Percival
  • Cool Infographics
  • Dan Sinker
  • David Walsh
  • Dmitry Dolzhenko
  • Dustin Curtis
  • eighty twenty
  • Elad Gil
  • Ellie Huxtable
  • Ethan Dalool
  • Ethan Marcotte
  • Exponential View
  • FAIL Blog
  • Founder Weekly
  • Geoffrey Huntley
  • Geoffrey Litt
  • Greg Mankiw
  • HeardThat Blog
  • Henrique Dias
  • Herman Martinus
  • Hypercritical
  • IEEE Spectrum
  • Investment Talk
  • Jaz
  • Jeff Geerling
  • Jonas Hietala
  • Josh Comeau
  • Lenny Rachitsky
  • Li Haoyi
  • Liz Danzico
  • Lou Plummer
  • Luke Wroblewski
  • Maggie Appleton
  • Matt Baer
  • Matt Stoller
  • Matthias Endler
  • Mert Bulan
  • Mind Matters
  • Mostly metrics
  • Naval Ravikant
  • News Letter
  • NextDraft
  • Non_Interactive
  • Not Boring
  • One Useful Thing
  • Phil Eaton
  • Product Market Fit
  • Readwise
  • ReedyBear
  • Robert Heaton
  • Rohit Patel
  • Ruben Schade
  • Sage Economics
  • Sam Altman
  • Sam Rose
  • selfh.st
  • Shtetl-Optimized
  • Simon schreibt
  • Slashdot
  • Small Good Things
  • Steph Ango
  • Stephen Wolfram
  • Steve Blank
  • Taylor Troesh
  • Telegram Blog
  • The Macro Compass
  • The Pomp Letter
  • thesephist
  • Thinking Deep & Wide
  • Tim Kellogg
  • Understanding AI
  • Wes Kao
  • 英文媒体
  • 英文推特
  • 英文独立博客
©2025 搞英语 → 看世界 | Design: Newspaperly WordPress Theme