本文首发于公众号:更AI (power_ai),欢迎关注,编程、AI干货及时送!
在这一章里,你被要求设计YouTube。这个问题的解决方案也可以被应用到设计像Netflix和Hulu这样的视频分享平台的其他面试问题上。图14-1展示了YouTube的主页。
YouTube看似简单:内容创作者上传视频,观众点击播放。真的就这么简单吗?并非如此。在简单之下有很多复杂的技术。让我们看一些2020年YouTube的一些令人印象深刻的统计数据,人口统计数据和有趣的事实 [1] [2]。
- 每月活跃用户总数:20亿。
- 每天观看的视频数量:50亿。
- 使用YouTube的美国成年人比例为73%。
- YouTube上有5000万创作者。
- YouTube的2019年全年广告收入为151亿美元,比2018年增长了36%。
- YouTube占所有移动互联网流量的37%。
- YouTube支持80种不同的语言。
从这些数据我们知道,YouTube是庞大的,全球化的,并且赚了很多钱。
第一步 - 理解问题并确定设计范围
如图14-1所示,除了观看视频,你还可以在YouTube上做更多的事情。例如,评论、分享或点赞视频,将视频保存到播放列表,订阅频道等。在45分钟或60分钟的面试中,我们无法设计所有内容。因此,提问以缩小设计范围是很重要的。
候选人:哪些功能是重要的?
面试官:上传视频和观看视频的能力。
候选人:我们需要支持哪些客户端?
面试官:移动应用程序,网络浏览器和智能电视。
候选人:我们有多少每日活跃用户?
面试官:500万
候选人:用户每天在产品上的平均使用时间是多少?
面试官:30分钟。
候选人:我们需要支持国际用户吗?
面试官:是的,大部分用户都是国际用户。
候选人:支持的视频分辨率是多少?
面试官:系统接受大部分的视频分辨率和格式。
候选人:需要加密吗?
面试官:是的
候选人:视频的文件大小有什么要求?
面试官:我们的平台主要针对小型和中型视频。最大允许的视频大小是1GB。
候选人:我们能利用亚马逊、谷歌或微软提供的一些现有云基础设施吗?
面试官:这是一个很好的问题。对于大多数公司来说,从零开始建设一切是不切实际的,建议利用一些现有的云服务。
在这一章中,我们将专注于设计具有以下特性的视频流服务:
- 快速上传视频的能力
- 平稳的视频流
- 改变视频质量的能力
- 低基础设施成本
- 高可用性,可扩展性和可靠性要求
- 支持的客户端:移动应用,网络浏览器和智能电视
信封背面的估计
以下估计基于许多假设,因此与面试官沟通以确保她在同一频道上是很重要的。
-
假设产品每天有500万活跃用户(DAU)。
-
用户每天观看5个视频。
-
10%的用户每天上传1个视频。
-
假设平均视频大小为300MB。
-
需要的每日存储空间总量:500万 * 10% * 300MB = 150TB
-
CDN成本。
- 当云CDN播放一个视频时,你需要支付CDN传出的数据费用。
- 让我们使用Amazon的CDN CloudFront进行成本估算(图14-2)[3]。假设100%的流量来自美国。每GB的平均成本是$0.02。为了简单起见,我们只计算视频流的成本。
- 500万 * 5个视频 * 0.3GB * $0.02 = 每天$150,000。
从粗略的成本估算,我们知道从CDN播放视频的成本很高。尽管云提供商愿意为大客户显著降低CDN成本,但成本仍然相当大。我们将在深度剖析中讨论减少CDN成本的方法。
第二步 - 提出高级设计并获得认同
正如之前的讨论,面试官推荐利用现有的云服务,而不是从零开始构建一切。我们将利用的云服务包括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)。
你不需要完全理解或甚至记住这些流媒体协议的名字,因为它们是需要特定领域知识的底层细节。这里要理解的重要一点是,不同的流媒体协议支持不同的视频编码和播放器。当我们设计一个视频流媒体播放服务时,我们必须选择正确的流媒体协议来支持我们的使用案例。要了解更多关于流媒体协议的信息,这里有一篇优秀的文章[7]。
视频是直接从CDN流媒体播放的。离你最近的边缘服务器将会传送视频。因此,延迟非常小。图14-7展示了视频流媒体播放的高级设计。
第三步 - 深入设计
在高级设计中,整个系统被分为两部分:视频上传流程和视频流媒体播放流程。在这一部分,我们将对这两个流程进行重要的优化,并引入错误处理机制。
视频转码
当你录制视频时,设备(通常是手机或相机)会为视频文件赋予一种特定格式。如果你希望视频在其他设备上顺畅播放,必须将视频编码为兼容的比特率和格式。比特率是指位在时间内的处理速度。较高的比特率通常意味着更高的视频质量。高比特率的流需要更多的处理能力和快速的互联网速度。
视频转码的重要性如下:
- 原始视频占用大量的存储空间。一小时长的高清视频,以每秒60帧的速度录制,可能会占用几百GB的空间。
- 许多设备和浏览器只支持某些类型的视频格式。因此,出于兼容性考虑,将视频编码为不同格式是很重要的。
- 为了确保用户在观看高质量视频的同时保持流畅的播放,将更高分辨率的视频提供给拥有高网络带宽的用户,而将较低分辨率的视频提供给带宽较低的用户,是一个好主意。
- 网络条件可能会改变,尤其是在移动设备上。为了确保视频连续播放,根据网络条件自动或手动切换视频质量对于流畅的用户体验是必要的。
虽然有许多类型的编码格式可供选择,但大多数编码格式都包含两部分:
- 容器:就像一个篮子,包含视频文件、音频和元数据。你可以通过文件扩展名来识别容器格式,比如.avi、.mov或.mp4。
- 编解码器:这些是压缩和解压缩算法,目标是在保持视频质量的同时减小视频大小。最常用的视频编解码器是H.264、VP9和HEVC。
有向无环图(DAG)模型
转码视频在计算上是昂贵且耗时的。此外,不同的内容创作者可能有不同的视频处理要求。例如,一些内容创作者需要在他们的视频上方加水印,一些自己提供缩略图图片,一些上传高清视频,而其他人则不会。
为了支持不同的视频处理流程并保持高并行性,重要的是增加一些抽象层次,并让客户端程序员定义要执行的任务。例如,Facebook的流媒体视频引擎使用有向无环图(DAG)编程模型,该模型在阶段中定义任务,因此它们可以顺序执行或并行执行[8]。在我们的设计中,我们采用了类似的DAG模型来实现灵活性和并行性。图14-8表示视频转码的DAG。
在图14-8中,原始视频被分割成视频、音频和元数据。以下是一些可以应用在视频文件上的任务:
- 检查:确保视频质量好且格式正常。
- 视频编码:将视频转换以支持不同的分辨率、编解码器、比特率等。图14-9显示了一个编码视频文件的例子。
- 缩略图。缩略图可以由用户上传,也可以由系统自动生成。
- 水印:视频顶部的图像叠加层,包含关于你的视频的识别信息。
视频转码架构
图14-10展示了我们提出的利用云服务的视频转码架构。
这个架构有六个主要组件:预处理器、DAG调度器、资源管理器、任务工作者、临时存储和编码视频作为输出。我们来仔细看看每一个组件。
预处理器
预处理器有4个职责:
- 视频分割。视频流被分割或进一步分割为更小的图像组(GOP)对齐。GOP是一组/一块按照特定顺序排列的帧。每块都是一个可以独立播放的单元,通常长度为几秒钟。
- 一些旧的移动设备或浏览器可能不支持视频分割。预处理器为旧客户端按照GOP对齐来分割视频。
- DAG生成。处理器根据客户端程序员编写的配置文件生成DAG。图14-12是一个简化的DAG表示,它有2个节点和1条边:
这个DAG表示是从下面的两个配置文件生成的(图14-13):
- 缓存数据。预处理器是分段视频的缓存。为了提高可靠性,预处理器将GOP和元数据存储在临时存储中。如果视频编码失败,系统可以使用持久化数据进行重试操作。
DAG调度器
DAG调度器将DAG图分割为任务阶段,并将它们放入资源管理器的任务队列中。图14-15展示了DAG调度器如何工作的一个例子。
如图14-15所示,原始视频被分割为三个阶段:阶段1:视频,音频和元数据。视频文件在阶段2进一步分割为两个任务:视频编码和缩略图。音频文件需要音频编码作为阶段2任务的一部分。
资源管理器
资源管理器负责管理资源分配的效率。它包含3个队列和一个任务调度器,如图14-17所示。
- 任务队列:这是一个包含待执行任务的优先级队列。
- 工作队列:这是一个包含工作利用信息的优先级队列。
- 运行队列:它包含当前正在运行的任务和执行任务的工作者的信息。
- 任务调度器:它选择最优的任务/工作者,并指示被选中的任务工作者执行任务。
资源管理器的工作流程如下:
- 任务调度器从任务队列中获取最高优先级的任务。
- 任务调度器从工作队列中获取最优的任务工作者来运行任务。
- 任务调度器指示选定的任务工作者运行任务。
- 任务调度器将任务/工作者信息绑定,并将其放入运行队列。
- 一旦任务完成,任务调度器会从运行队列中移除该任务。
任务工作者
任务工作者执行在DAG中定义的任务。如图14-19所示,不同的任务工作者可能会执行不同的任务。
临时存储
这里使用了多种存储系统。选择哪种存储系统取决于数据类型、数据大小、访问频率、数据寿命等因素。例如,元数据经常被工作者访问,数据大小通常较小。因此,将元数据缓存在内存中是个好主意。对于视频或音频数据,我们将它们放入Blob存储中。一旦相应的视频处理完成,临时存储中的数据就会被清理掉。
编码后的视频
编码后的视频是编码流水线的最终输出。这是输出的一个例子:funny_720p.mp4。
系统优化
到此为止,你应该已经对视频上传流程、视频流式传输和视频转码有了深入的理解。接下来,我们将通过优化来完善系统,包括速度、安全性和节省成本。
速度优化:并行化视频上传
将整个视频作为一个单位进行上传效率不高。我们可以通过GOP对齐方式将视频分割成较小的片段,如图14-22所示。
这样可以在之前的上传失败时实现快速可恢复的上传。由客户端实现通过GOP分割视频文件的工作,可以提高上传速度,如图14-23所示。
速度优化:将上传中心设置得靠近用户
提高上传速度的另一种方式是在全球范围内设置多个上传中心(图14-24)。美国的人可以将视频上传到北美的上传中心,中国的人可以将视频上传到亚洲的上传中心。为了实现这一点,我们使用CDN作为上传中心。
速度优化:全面并行化
要实现低延迟需要付出严肃的努力。另一种优化方法是建立一个松散耦合的系统并实现高并行性。
我们的设计需要做一些修改才能实现高并行性。让我们放大看一下视频如何从原始存储转移到CDN的流程。流程如图14-25所示,揭示出输出依赖于前一步的输入。这种依赖性使并行性变得困难。
为了使系统更松散地耦合,我们引入了消息队列,如图14-26所示。让我们用一个例子来解释消息队列如何使系统更松散地耦合。
- 在引入消息队列之前,编码模块必须等待下载模块的输出。
- 在引入消息队列之后,编码模块不再需要等待下载模块的输出。如果消息队列中有事件,编码模块可以并行执行这些任务。
安全性优化:预签名上传URL
安全性是任何产品的最重要方面之一。为了确保只有授权的用户将视频上传到正确的位置,我们引入了预签名URL,如图14-27所示。
上传流程更新如下:
- 客户端向API服务器发起HTTP请求,以获取预签名URL,该URL给予访问URL中标识的对象的访问权限。预签名URL一词用于上传文件到Amazon S3。其他云服务提供商可能使用不同的名称。例如,Microsoft Azure blob存储支持同样的功能,但它将其称为“共享访问签名”[10]。
- API服务器返回一个预签名URL。
- 一旦客户端收到响应,它就可以使用预签名URL上传视频。
安全性优化:保护你的视频
许多内容创作者不愿意在线发布视频,因为他们担心他们的原创视频会被盗。为了保护版权视频,我们可以采用以下三种安全选项之一:
- 数字版权管理(DRM)系统:三大主要的DRM系统是苹果的FairPlay,谷歌的Widevine和微软的PlayReady。
- AES加密:你可以加密视频并配置授权策略。在播放时将解密加密的视频。这确保只有授权用户才能观看加密视频。
- 视觉水印:这是一个在你的视频上方覆盖的图像,包含你的视频的识别信息。这可以是你的公司标志或公司名称。
节省成本的优化
CDN是我们系统的关键组件。它确保了全球范围内的快速视频传输。然而,从信封背面的计算中,我们知道CDN是昂贵的,特别是当数据大小较大时。我们如何降低成本呢?
以前的研究表明,YouTube的视频流遵循长尾分布[11][12]。这意味着一些热门视频被频繁访问,但许多其他视频的观看人数少或没有。基于这个观察,我们实施了一些优化:
- 只从CDN提供最热门的视频,其他视频从我们的高容量存储视频服务器提供(如图14-28所示)。
2. 对于不太热门的内容,我们可能不需要存储许多编码后的视频版本。短视频可以按需编码。
3. 一些视频只在某些地区受欢迎。没有必要将这些视频分发到其他地区。
4. 建立你自己的CDN,像Netflix一样,与互联网服务提供商(ISP)合作。建立你自己的CDN是一个大项目;然而,对于大型流媒体公司来说,这可能是有意义的。ISP可以是Comcast,AT&T,Verizon或其他互联网提供商。ISP分布在全球各地,离用户很近。通过与ISP合作,你可以改善观看体验并减少带宽费用。
所有这些优化都基于内容的受欢迎程度、用户访问模式、视频大小等。在进行任何优化之前,分析历史观看模式是很重要的。以下是一些关于这个话题的有趣文章:[12][13]。
错误处理
对于大规模的系统,系统错误是不可避免的。要建立一个高度容错的系统,我们必须优雅地处理错误并快速从中恢复。存在两种类型的错误:
- 可恢复错误。对于像视频段无法转码这样的可恢复错误,一般的想法是重试操作几次。如果任务继续失败,系统认为它无法恢复,那么返回适当的错误码给客户端。
- 不可恢复错误。对于像视频格式错误这样的不可恢复错误,系统停止与视频相关的正在运行的任务,并返回适当的错误码给客户端。
以下的指南涵盖了每个系统组件的典型错误:
-
上传错误:重试几次。
-
分割视频错误:如果旧版本的客户端无法按GOP对齐分割视频,整个视频会传送到服务器。在服务器端完成视频分割的工作。
-
转码错误:重试。
-
预处理器错误:重新生成DAG图。
-
DAG调度器错误:重新调度任务。
-
资源管理器队列停止:使用副本。
-
任务工作者停止:在新的工作者上重试任务。
-
API服务器停止:API服务器是无状态的,所以请求将被定向到另一个API服务器。
-
元数据缓存服务器停止:数据被多次复制。如果一个节点宕机,你仍然可以访问其他节点获取数据。我们可以启动一个新的缓存服务器来替换已死的服务器。
-
元数据数据库服务器停止:
- 主服务器停止。如果主服务器停止,提升一个从属服务器作为新的主服务器。
- 从属服务器停止。如果从属服务器停止,你可以使用另一个从属服务器进行读取,并启动另一个数据库服务器来替换已死的服务器。
步骤4 - 总结
在本章中,我们介绍了类似YouTube的视频流媒体服务的架构设计。如果在面试结束时还有额外的时间,以下是一些额外的要点:
-
扩展API层:因为API服务器是无状态的,所以水平扩展API层很容易。
-
扩展数据库:你可以谈论数据库复制和分片。
-
直播流:它指的是视频如何被实时记录和广播的过程。虽然我们的系统并没有专门为直播流设计,但直播流和非直播流有一些相似之处:都需要上传,编码和流式传输。显著的区别包括:
- 直播流对延迟的要求更高,因此可能需要不同的流传输协议。
- 因为小块的数据已经在实时处理,直播流对并行性的要求较低。
- 直播流需要不同的错误处理集合。任何花费太多时间的错误处理都是不可接受的。
-
视频下架:侵犯版权、色情或其他非法行为的视频应该被移除。系统在上传过程中可以发现一些,而其他一些可能通过用户标记来发现。
恭喜你走到了这一步!现在给自己一声赞扬。干得好!
参考资料
[1] YouTube 数据统计:https://www.omnicoreagency.com/youtube-statistics/
[2] 2019年 YouTube 用户数据统计:
https://blog.hubspot.com/marketing/youtube-demographics
[3] Cloudfront 计费信息:https://aws.amazon.com/cloudfront/pricing/
[4] Netflix 与 AWS:https://aws.amazon.com/solutions/case-studies/netflix/
[5] Akamai 主页:https://www.akamai.com/
[6] 二进制大对象:https://en.wikipedia.org/wiki/Binary_large_object
[7] 你需要知道的关于流媒体协议的信息:
https://www.dacast.com/blog/streaming-protocols/
[8] SVE:Facebook规模的分布式视频处理:
https://www.cs.princeton.edu/~wlloyd/papers/sve-sosp17.pdf
[9] 微博视频处理架构(中文):
https://www.upyun.com/opentalk/399.html
[10] 使用共享访问签名委派权限:
https://docs.microsoft.com/en-us/rest/api/storageservices/delegate-access-with-shared-access-
signature
[11] YouTube 的早期员工关于 YouTube 扩展性的讲解:https://www.youtube.com/watch?
v=w5WVu624fY8
[12] 理解互联网短视频分享的特性:基于 YouTube 的测量研究。
https://arxiv.org/pdf/0707.3670.pdf
[13] 对 Open Connect 的内容受欢迎程度:
https://netflixtechblog.com/content-popularity-for-open-connect-b86d56f613b
你好,我是拾叁,7年开发老司机、互联网两年外企5年。怼得过阿三老美,也被PR comments搞崩溃过。这些年我打过工,创过业,接过私活,也混过upwork。赚过钱也亏过钱。一路过来,给我最深的感受就是不管学什么,一定要不断学习。只要你能坚持下来,就很容易实现弯道超车!所以,不要问我现在干什么是否来得及。如果你还没什么方向,可以先关注我[公众号:更AI (power_ai)],这里会经常分享一些前沿资讯和编程知识,帮你积累弯道超车的资本。