第十四章:设计 YouTube (Design YouTube)
在本章中,你被要求设计 YouTube。这个问题的解决方案可以应用于其他面试问题,比如设计视频共享平台,例如 Netflix 和 Hulu。
YouTube看起来很简单:内容创作者上传视频,观众点击播放。真的那么简单吗?并不完全如此。其背后有很多复杂的技术。让我们看看一些YouTube在2020年的令人印象深刻的统计数据、人口统计和趣闻 [1] [2]。
- 每月活跃用户总数:20亿。
- 每天观看的视频数量:50亿。
- 73%的美国成年人使用YouTube。
- YouTube上有5000万创作者。
- YouTube的广告收入在2019年全年达到151亿美元,较2018年增长了36%。
- YouTube占据了所有移动互联网流量的37%。
- YouTube支持80种不同语言。
从这些统计数据来看,我们知道YouTube是庞大的、全球性的,并且盈利丰厚。
第 1 步 - 理解问题并确定设计范围
如图14-1所示,除了观看视频,用户在YouTube上还可以执行更多操作,例如评论、分享或点赞视频,将视频保存到播放列表,订阅频道等。显然,在45分钟或60分钟的面试中不可能设计所有这些功能。因此,提出一些问题来缩小设计范围至关重要。
候选人:哪些功能是重点?
面试官:上传视频和观看视频的能力。
候选人:需要支持哪些客户端?
面试官:移动应用程序、网页浏览器和智能电视。
候选人:我们每天有多少活跃用户?
面试官:500万。
候选人:用户平均每天在产品上花费多少时间?
面试官:30分钟。
候选人:是否需要支持国际用户?
面试官:是的,很多用户是国际用户。
候选人:支持哪些视频分辨率?
面试官:系统接受大多数视频分辨率和格式。
候选人:是否需要加密?
面试官:是的。
候选人:视频文件有大小限制吗?
面试官:我们平台侧重于小型和中型视频,最大允许上传的文件大小为1GB。
候选人:我们可以使用Amazon、Google或Microsoft提供的云基础设施吗?
面试官:这是个好问题。对大多数公司来说,从头开始构建所有功能是不现实的,建议利用现有的云服务。
在本章节中,我们将重点设计具有以下功能的视频流服务:
- 快速上传视频
- 流畅的视频播放
- 可切换的视频质量
- 低基础设施成本
- 高可用性、可扩展性和可靠性要求
- 支持客户端:移动应用、网页浏览器和智能电视
粗略的估算
以下估算基于许多假设,因此与面试官保持沟通,确保她与您在同一页面上非常重要。
- 假设产品有500万每日活跃用户(DAU)。
- 用户每天观看5个视频。
- 10%的用户每天上传1个视频。
- 假设平均视频大小为300 MB。
- 每天所需的总存储空间:500万 * 10% * 300 MB = 150 TB。
- CDN成本:
- 当云CDN提供视频时,会对传输出去的数据进行收费。
- 我们使用Amazon的CDN CloudFront进行成本估算(如图14-2所示) [3]。假设100%的流量来自美国,平均每GB费用为0.02美元。为简单起见,我们只计算视频流的费用。
- 500万 * 5个视频 * 0.3 GB * 0.02美元 = 每天150,000美元。
从这个粗略的成本估算中可以看出,通过CDN提供视频的费用是巨大的。即使云提供商愿意为大客户大幅降低CDN成本,但成本仍然相当高。我们将在深入分析中讨论如何降低CDN成本的方案。
第2步 - 提出高层设计并获得认可
如之前讨论的那样,面试官建议利用现有的云服务,而不是从头构建所有内容。我们将使用CDN和Blob存储等云服务。可能有些读者会问,为什么不自己构建所有服务呢?以下是原因:
- 系统设计面试 并不要求从头构建所有东西。在有限的时间内,选择合适的技术来正确完成工作比详细解释技术如何工作更重要。例如,在面试中提到使用Blob存储来存储原始视频已经足够了,深入探讨Blob存储的详细设计可能会显得过于复杂。
- 构建可扩展的Blob存储或CDN 极其复杂且成本高昂。即使像Netflix或Facebook这样的大公司也不会自己构建所有系统。Netflix依赖Amazon的云服务 [4],而Facebook则使用Akamai的CDN [5]。
在高层设计中,系统由三个主要组件组成(见图14-3):
客户:您可以在电脑、手机和智能电视上观看YouTube。
CDN(内容分发网络):视频存储在CDN中。当您点击播放时,视频从CDN流传输。
API服务器:除视频流传输外的所有功能都通过API服务器处理。这包括推荐视频、生成视频上传URL、更新元数据数据库和缓存、用户注册等。
在问答环节中,面试官对以下两个流程表现出兴趣:
- 视频上传流程
- 视频流传输流程
接下来,我们将探讨每个流程的高层设计。
视频上传流程
图14-4展示了视频上传的高层设计。
其组件包括以下部分:
- 用户:用户可以通过电脑、手机或智能电视等设备观看YouTube。
- 负载均衡器:负载均衡器均匀地将请求分配给API服务器。
- API服务器:除视频流传输外,所有用户请求都通过API服务器处理。
- 元数据数据库:视频的元数据存储在元数据数据库中。为了满足性能和高可用性要求,数据库被分片和复制。
- 元数据缓存:为提高性能,视频元数据和用户对象被缓存。
- 原始存储:使用二进制大对象(BLOB)存储系统存储原始视频。正如维基百科中关于BLOB存储的描述:“二进制大对象(BLOB)是数据库管理系统中作为单一实体存储的二进制数据集合” [6]。
- 转码服务器:视频转码也称为视频编码,是将视频格式转换为其他格式(如MPEG、HLS等)的过程,以提供不同设备和带宽条件下最佳的视频流。
- 转码存储:这是一种用于存储转码后视频文件的BLOB存储。
- CDN:视频缓存在CDN中。当您点击播放按钮时,视频从CDN流传输。
- 完成队列:这是一个消息队列,存储视频转码完成事件的信息。
- 完成处理器:由一组工作程序组成,它们从完成队列中提取事件数据并更新元数据缓存和数据库。
现在我们了解了每个组件的功能,接下来我们来看视频上传流程的工作方式。该流程分为两个并行运行的过程: a. 上传实际视频。 b. 更新视频元数据。元数据包含视频URL、大小、分辨率、格式、用户信息等。
流程 a:上传实际视频
图14-5展示了如何上传实际视频。具体解释如下:
- 视频上传到原始存储中。
- 转码服务器从原始存储中获取视频,并开始转码。
- 一旦转码完成,接下来会并行执行以下两个步骤:
- 3a. 转码后的视频被发送到转码存储。
- 3b. 转码完成事件被加入到完成队列中。
- 3a.1. 转码后的视频会被分发到CDN中。
- 3b.1. 完成处理器由一组工作程序组成,这些程序会持续从队列中拉取事件数据。
- 3b.1.a. 和 3b.1.b. 当视频转码完成时,完成处理器会更新元数据数据库和缓存。
- API服务器通知客户端视频已成功上传,并可以开始流媒体播放。
流程 b:更新元数据
当文件上传到原始存储时,客户端会并行发送请求更新视频元数据,如图14-6所示。该请求包含视频元数据信息,如文件名、大小、格式等。API服务器随后更新元数据缓存和数据库。
视频流播放流程
当你在YouTube上观看视频时,通常视频会立即开始播放,而不需要等到整个视频下载完成。下载意味着整个视频被复制到你的设备上,而流媒体播放意味着你的设备会持续接收来自远程源的视频流。当你观看流媒体视频时,客户端会一次加载少量数据,这样你可以立即并连续地观看视频。
在讨论视频流播放流程之前,我们先了解一个重要概念:流媒体协议。这是用于控制视频流传输的标准化方式。常见的流媒体协议包括:
- MPEG-DASH:MPEG代表“动态图像专家组”,DASH代表“基于HTTP的动态自适应流媒体”。
- Apple HLS:HLS代表“HTTP实时流媒体”。
- Microsoft Smooth Streaming(微软平滑流媒体)。
- Adobe HTTP Dynamic Streaming (HDS)(Adobe HTTP动态流媒体)。
你不需要完全理解或记住这些流媒体协议的名称,因为它们涉及底层细节,通常需要特定领域的知识。这里重要的是要理解,不同的流媒体协议支持不同的视频编码和播放播放器。当我们设计视频流媒体服务时,必须选择合适的流媒体协议来满足我们的用例需求。想要了解更多关于流媒体协议的内容,这里有一篇优秀的文章 [7]。
视频直接从CDN(内容分发网络)进行流传输。距离你最近的边缘服务器将会为你提供视频,因此延迟非常低。图14-7展示了视频流播放的高层设计。
第 3 步 - 设计深入探讨
在高层设计中,整个系统被分为两个部分:视频上传流程和视频流播放流程。在本节中,我们将对这两个流程进行细化,提出重要的优化措施,并介绍错误处理机制。
视频转码
当你录制视频时,设备(通常是手机或摄像机)会给视频文件赋予特定的格式。如果你希望视频能够在其他设备上流畅播放,必须将视频编码为兼容的比特率和格式。比特率是指数据在单位时间内处理的速度。通常来说,比特率越高,视频质量越高。高比特率的流媒体需要更强的处理能力和更快的网络速度。
视频转码的重要性在于:
- 原始视频会占用大量存储空间。以60帧每秒录制的一个小时的高清晰度视频可能占用数百GB的空间。
- 许多设备和浏览器仅支持某些特定类型的视频格式。因此,出于兼容性的考虑,将视频编码为不同格式是必要的。
- 为了确保用户能够观看高质量的视频,同时保持流畅的播放体验,最好为带宽高的用户提供高分辨率视频,为带宽低的用户提供低分辨率视频。
- 网络状况会发生变化,尤其是在移动设备上。为了确保视频能够持续播放,基于网络状况自动或手动切换视频质量对流畅的用户体验至关重要。
有多种编码格式可供选择;但它们通常包含两部分:
- 容器:容器类似于一个篮子,它包含视频文件、音频和元数据。你可以通过文件扩展名识别容器格式,比如.avi、.mov或.mp4。
- 编码器(Codecs):编码器是压缩和解压缩算法,旨在在保持视频质量的同时减少视频文件的大小。最常用的视频编码器包括H.264、VP9和HEVC。
有向无环图(DAG)模型
视频转码是一项计算密集且耗时的任务。此外,不同的内容创作者可能有不同的视频处理需求。例如,有些内容创作者需要在视频上添加水印,有些提供自己的缩略图,还有些上传高清视频,而另一些则没有。
为了支持不同的视频处理流程并保持高并行性,引入一定的抽象层次,让客户端程序员定义要执行的任务是非常重要的。例如,Facebook的流媒体视频引擎使用了**有向无环图(DAG)**编程模型,该模型将任务划分为多个阶段,这些任务可以顺序或并行执行 [8]。在我们的设计中,我们采用了类似的DAG模型,以实现灵活性和并行性。图14-8展示了视频转码的DAG模型。
在图14-8中,原始视频被分为视频、音频和元数据。以下是一些可以应用于视频文件的任务:
- 检查:确保视频质量良好且未损坏。
- 视频编码:将视频转换为支持不同分辨率、编码器、比特率等的格式。图14-9展示了视频编码文件的示例。
- 缩略图:缩略图可以由用户上传,或者由系统自动生成。
- 水印:在视频顶部叠加的图像,包含视频的识别信息。
视频转码架构
图14-10展示了利用云服务的提议视频转码架构。该架构有六个主要组件:预处理器、DAG调度器、资源管理器、任务工作者、临时存储以及最终的编码视频输出。我们来仔细看看每个组件的作用。
预处理器 (Preprocessor)
预处理器有四项主要职责:
- 视频分割:视频流会被分割,或进一步按照GOP(图像组)对齐方式分割为更小的单位。GOP是按特定顺序排列的一组或一段帧,每一段是一个可以独立播放的单位,通常持续几秒钟。
- 支持旧设备:一些旧的移动设备或浏览器可能不支持视频分割。预处理器会按GOP对齐方式为旧客户端分割视频。
- DAG生成:处理器根据客户端程序员编写的配置文件生成DAG。图14-12展示了一个简化的DAG表示,它包含2个节点和1条边:
该DAG表示是从下面的两个配置文件中生成的(见图14-13)[9]。
- 缓存数据:预处理器作为分段视频的缓存。为了提高可靠性,预处理器会将GOP(图像组)和元数据存储在临时存储中。如果视频编码失败,系统可以使用持久化的数据进行重试操作。
DAG调度器 (DAG scheduler)
DAG调度器将DAG图划分为多个任务阶段,并将这些任务放入资源管理器中的任务队列。图14-15展示了DAG调度器的工作方式。
如图14-15所示,原始视频被分为三个阶段:
- 第1阶段:视频、音频和元数据。
- 第2阶段:视频文件进一步被拆分为两个任务:视频编码和缩略图生成。音频文件则需要进行音频编码,这也是第2阶段任务的一部分。
资源管理器 (Resource manager)
资源管理器负责管理资源分配的效率。它包含三个队列和一个任务调度器,如图14-17所示。
- 任务队列:这是一个优先级队列,包含待执行的任务。
- 工作者队列:这是一个优先级队列,包含工作者的利用信息。
- 运行队列:包含当前正在运行的任务及其运行任务的工作者信息。
- 任务调度器:选择最优的任务和工作者,并指示选定的任务工作者执行任务。
资源管理器的工作流程如下:
- 任务调度器从任务队列中获取优先级最高的任务。
- 任务调度器从工作者队列中选择最优的任务工作者来运行该任务。
- 任务调度器指示选定的任务工作者运行该任务。
- 任务调度器将任务和工作者信息绑定,并放入运行队列中。
- 任务完成后,任务调度器从运行队列中移除该任务。
任务工作者 (Task workers)
任务工作者执行在DAG中定义的任务。不同的任务工作者可以运行不同的任务,如图14-19所示。
临时存储 (Temporary storage)
这里使用了多个存储系统。存储系统的选择取决于数据类型、数据大小、访问频率、数据生命周期等因素。例如,元数据被工作者频繁访问,数据大小通常较小。因此,将元数据缓存到内存中是一个不错的选择。对于视频或音频数据,我们将其存放在Blob存储中。临时存储中的数据将在相应的视频处理完成后被释放。
编码视频 (Encoded video)
编码视频是编码管道的最终输出。例如,输出文件名可能是:funny_720p.mp4。
系统优化 (System optimizations)
到目前为止,你应该对视频上传流程、视频流播放流程和视频转码有了较好的理解。接下来,我们将通过优化来改进系统,包括速度、安全性和成本节约。
速度优化:并行化视频上传 (Speed optimization: parallelize video uploading)
将视频作为一个整体单位上传效率不高。我们可以通过GOP对齐将视频拆分为更小的块,如图14-22所示。这允许在先前上传失败时快速恢复上传。
通过GOP拆分视频文件的工作可以由客户端实现,以提高上传速度,如图14-23所示。
速度优化:将上传中心设置在用户附近 (Speed optimization: place upload centers close to users)
另一种提高上传速度的方法是在全球范围内建立多个上传中心(见图14-24)。美国的用户可以将视频上传到北美上传中心,而中国的用户则可以将视频上传到亚洲上传中心。为此,我们使用CDN作为上传中心。
速度优化:到处实现并行性 (Speed optimization: parallelism everywhere)
实现低延迟需要付出大量努力。另一种优化是构建一个松耦合的系统,并启用高并行性。
我们的设计需要进行一些修改,以实现高并行性。让我们深入了解视频从原始存储传输到CDN的流程。图14-25显示,输出依赖于前一步的输入,这种依赖性使得并行性变得困难。
为了使系统更加松耦合,我们引入了消息队列,如图14-26所示。让我们用一个例子来解释消息队列如何使系统更加松耦合。
- 在引入消息队列之前,编码模块必须等待下载模块的输出。
- 在引入消息队列之后,编码模块不再需要等待下载模块的输出。如果消息队列中有事件,编码模块可以并行执行这些任务。
安全优化:预签名上传URL (Safety optimization: pre-signed upload URL)
安全性是任何产品最重要的方面之一。为了确保只有授权用户才能将视频上传到正确的位置,我们引入了预签名的URL,如图14-27所示。
上传流程更新如下:
- 客户端向API服务器发起HTTP请求,以获取预签名URL,该URL授予访问URL中标识的对象的权限。预签名URL一词通常用于上传文件到Amazon S3。其他云服务提供商可能使用不同的名称。例如,Microsoft Azure Blob存储支持相同的功能,但称之为“共享访问签名” [10]。
- API服务器响应一个预签名URL。
- 一旦客户端收到响应,它就使用预签名URL上传视频。
安全优化:保护您的视频 (Safety optimization: protect your videos)
许多内容创作者不愿意在网上发布视频,因为他们担心自己的原创视频会被盗。为了保护版权视频,我们可以采用以下三种安全选项之一:
- 数字版权管理(DRM)系统:三种主要的DRM系统是Apple FairPlay、Google Widevine和Microsoft PlayReady。
- AES加密:您可以加密视频并配置授权策略。加密视频将在播放时解密。这确保只有授权用户才能观看加密视频。
- 视觉水印:这是覆盖在视频上的图像,包含您视频的识别信息,可以是您的公司徽标或公司名称。
成本节约优化 (Cost-saving optimization)
CDN是我们系统的一个关键组成部分,它确保全球范围内的视频快速交付。然而,从简单的计算来看,我们知道CDN成本昂贵,尤其是在数据量较大时。我们如何降低成本呢? 先前的研究表明,YouTube视频流遵循长尾分布 [11] [12]。这意味着少数热门视频的访问频率很高,而许多其他视频的观众很少或没有。基于这一观察,我们实施了一些优化:
- 仅从CDN提供最受欢迎的视频,其他视频则从我们高容量存储的视频服务器提供(见图14-28)。
对于不太受欢迎的内容,我们可能不需要存储多个编码视频版本。短视频可以按需编码。
一些视频仅在特定地区受欢迎,没必要将这些视频分发到其他地区。
像Netflix一样构建自己的CDN,并与互联网服务提供商(ISP)合作。构建CDN是一个庞大的项目;然而,对于大型流媒体公司来说,这是有意义的。ISP可以是 Comcast、AT&T、Verizon 或其他互联网提供商。ISP遍布全球,离用户很近。通过与ISP合作,您可以改善观看体验并降低带宽费用。
所有这些优化都基于内容受欢迎程度、用户访问模式、视频大小等。在进行任何优化之前,分析历史观看模式是非常重要的。以下是一些关于此主题的有趣文章:[12] [13]。
错误处理 (Error handling)
对于大规模系统,系统错误是不可避免的。为了构建一个高容错的系统,我们必须优雅地处理错误并快速恢复。错误主要有两种类型:
- 可恢复错误:对于可恢复错误,例如视频片段无法转码,通常的做法是重试操作几次。如果任务仍然失败且系统认为无法恢复,则返回适当的错误代码给客户端。
- 不可恢复错误:对于不可恢复错误,例如格式错误的视频,系统停止与该视频相关的正在运行的任务,并返回适当的错误代码给客户端。
每个系统组件的典型错误由以下操作手册涵盖:
- 上传错误:重试几次。
- 视频拆分错误:如果旧版客户端无法按GOP对齐拆分视频,则将整个视频传递给服务器。视频拆分的工作在服务器端完成。
- 转码错误:重试。
- 预处理器错误:重新生成DAG图。
- DAG调度器错误:重新调度任务。
- 资源管理器队列故障:使用副本。
- 任务工作者故障:在新工作者上重试任务。
- API服务器故障:API服务器是无状态的,因此请求将被指向不同的API服务器。
- 元数据缓存服务器故障:数据被多次复制。如果一个节点宕机,您仍然可以访问其他节点以获取数据。我们可以启动一个新的缓存服务器来替换故障的服务器。
- 元数据数据库服务器故障:
- 主节点故障:如果主节点宕机,则提升一个从节点作为新的主节点。
- 从节点故障:如果一个从节点宕机,您可以使用另一个从节点进行读取,并启动另一个数据库服务器以替换故障的节点。
第 4 步 - 总结
在本章中,我们展示了视频流服务(如YouTube)的架构设计。如果面试结束时还有额外时间,这里有一些补充要点:
- 扩展API层:由于API服务器是无状态的,因此可以轻松地进行横向扩展。
- 扩展数据库:可以讨论数据库的复制和分片。
- 直播:指的是视频如何实时录制和广播的过程。尽管我们的系统并不是专门为直播设计的,但直播和非直播在某些方面是相似的:都需要上传、编码和流媒体传输。主要区别在于:
- 直播对延迟的要求更高,因此可能需要不同的流媒体协议。
- 直播对并行处理的要求较低,因为小块数据已经实时处理。
- 直播需要不同的错误处理机制。任何耗时过长的错误处理都是不可接受的。
- 视频下架:侵犯版权、色情或其他非法行为的视频应被删除。有些可以在上传过程中被系统发现,而另一些可能通过用户举报被发现。
恭喜你走到这一步!现在给自己一个赞,做得很好!
参考文献
[1] YouTube by the numbers: https://www.omnicoreagency.com/youtube-statistics/
[2] 2019 YouTube Demographics: https://blog.hubspot.com/marketing/youtube-demographics
[3] Cloudfront Pricing: https://aws.amazon.com/cloudfront/pricing/
[4] Netflix on AWS: https://aws.amazon.com/solutions/case-studies/netflix/
[5] Akamai homepage: https://www.akamai.com/
[6] Binary large object: https://en.wikipedia.org/wiki/Binary_large_object
[7] Here’s What You Need to Know About Streaming Protocols: https://www.dacast.com/blog/streaming-protocols/
[8] SVE: Distributed Video Processing at Facebook Scale: https://www.cs.princeton.edu/~wlloyd/papers/sve-sosp17.pdf
[9] Weibo video processing architecture (in Chinese): https://www.upyun.com/opentalk/399.html
[10] Delegate access with a shared access signature: https://docs.microsoft.com/en-us/rest/api/storageservices/delegate-access-with-shared-accesssignature
[11] YouTube scalability talk by early YouTube employee: https://www.youtube.com/watch?v=w5WVu624fY8
[12] Understanding the characteristics of internet short video sharing: A youtube-based measurement study. https://arxiv.org/pdf/0707.3670.pdf
[13] Content Popularity for Open Connect: https://netflixtechblog.com/content-popularity-for-open-connect-b86d56f613b