在本章中,你被要求设计一个像YouTube那样的系统。与这个面试问题类似的还有:设计一个类似Netflix和Hulu的视频分享平台,它们的解决方案是相同的。YouTube看起来很简单:内容创作者上传视频,观看者点击视频后播放。它真的这么简单吗?并不是。在这种简单的背后有很多复杂的技术在提供支持。我们来看截至2020年关于YouTube的一些令人印象深刻的统计数据和有趣的事实。
•月活用户总数:20亿。
•每天被观看的视频数量:50亿。
•73%的美国成年人使用YouTube。
•YouTube有5000万内容创作者。
•2019年YouTube全年的广告收入是151亿美元,相比2018年增长了36%。
•YouTube的流量占移动互联网总流量的37%。
•YouTube支持80种不同的语言。
除了看视频,在YouTube上还可以做很多其他事情。比如,对视频发表评论,将视频分享给其他人,或者为视频点赞,将视频保存到播放列表,以及订阅频道等。面试场景不能完整设计,本章我们会专注于设计具有如下功能的视频流服务:
•可以快速上传视频。
•流畅的视频播放效果。
•可以调整视频质量。
•基础设施费用低。
•具有高可用性、可扩展性和可靠性。
•支持的客户端包括移动应用、网页浏览器和智能电视。
1.1 封底估算
下面的估算要基于很多假设,所以与面试官沟通,以确保你们双方的理解一致是很重要的。•该产品有500万日活用户(DAU)。
•每个用户平均每天看5个视频。
•10%的用户每天上传1个视频。
•假设视频文件的平均大小是300MB,则每天总共需要的存储空间为:500万×10%×300MB=150TB。
•CDN成本。当由CDN来提供视频服务时,我们要为从CDN传输出去的数据付费。我们使用亚马逊的CDN CloudFront来进行成本估算,假设平均每GB流量的价格是0.14人民币。为了简单起见,我们只计算视频流服务的成本。
500万×5个视频×0.3GB×0.14人民币=1050,000人民币
根据这个粗略的成本估算,我们发现通过CDN来提供视频要花很多钱。即使云服务提供商愿意为大客户降低CDN成本,但这个费用还是很高。我们会在第3节中谈论降低CDN成本的办法。
2. 高层级设计
之前已经讨论过,面试官建议使用已有的云服务而不是从头构建所有的东西。CDN和Blob存储是我们将会用到的云服务。有些读者可能会问,为什么不自己构建所有服务呢?原因如下:•系统设计面试不要求我们从头开始构建一切。在有限的面试时间里,选择正确的技术来正确地完成工作比详细解释技术的原理更重要。举个例子,对于面试来说,提到用Blob存储来存储源视频就足够了。要是谈论Blob存储的详细设计可能就有点画蛇添足了。
•构建一个可扩展的Blob存储或者CDN是极其复杂和昂贵的。即使像Netflix或者Facebook这样的大公司也没有自己构建所有的东西。Netflix使用了亚马逊的云服务,Facebook使用Akamai的CDN。总的来看,我们的这个系统由3部分组成(见图-1)。
图-1
**客户端:**你可以在电脑、手机和智能电视上访问这个类YouTube系统。
**CDN:**视频存储在CDN中。当你点击“播放”按钮时,视频流从CDN中被传输出来。
**API服务器:**除了视频流之外的所有请求都被发往API服务器,包括推荐视频、生成视频上传URL、更新元数据数据库和缓存、用户注册等。
•视频上传流程。
•视频的流式传输流程(Video Streaming Flow)。
我们会逐一讲解这两个流程的高层级设计。
2.1 视频上传
流程图-2展示了视频上传流程的高层级设计。它由如下组件组成。•用户:用户通过计算机、手机或者智能电视等设备访问系统。
•负载均衡器:在API服务器之间均匀地分配请求。
•API服务器:除视频流的传输外,所有用户的请求都要经过API服务器。
•元数据数据库:视频元数据存储在元数据数据库中。该数据库被分片和复制以满足性能和高可用性的需求。
•元数据缓存:为了实现更好的性能,视频元数据和用户对象被缓存。
•原始存储:使用Blob存储系统来存储原始视频。维基百科上关于Blob存储的描述为:“Blob(Binary Large Object,二进制大对象)是一个二进制数据的集合,在数据库管理系统中是作为一个单独实体来存储的”。
•转码服务器:视频转码也叫作视频编码,是把视频由一种格式转换成其他格式(MPEG、HLS等)的过程,它能基于不同的设备和带宽提供最合适的视频流。
•转码存储:一个Blob存储系统,用于存储转码后的视频文件。
•CDN:视频在CDN中缓存。当你点击“播放”按钮时,视频就会从CDN中进行流式传输。
•完成队列:它是一个消息队列,存储关于视频转码完成事件的信息。
•完成处理器:它由一系列Worker组成,它们从完成队列中拉取事件数据并更新元数据缓存和数据库。
图-2
我们了解了每个组件,现在来看视频上传流程是如何进行的。整个流程分为如下两个并行运行的子流程:
a.上传实际视频。
b.更新视频元数据。
元数据包含视频的URL、大小、分辨率、格式、用户数据等信息。
流程a:上传实际视频
图-3展示了如何上传实际视频。
图-3
1.视频被上传到原始存储里。
2.转码服务器从原始存储里获取视频并开始转码。
3.一旦转码结束,下面两个步骤(3a与3b)就开始并行执行。
3a.转码后的视频被发到转码存储里。
3b.转码完成事件被加入完成队列并开始排队。
3a.1.转码后的视频被分配到CDN中。
3b.1.在完成处理器中有一组Worker不断地从完成队列中拉取事件数据。
3b.1.a.和3b.1.b.当视频转码完成后,完成处理器更新元数据数据库和元数据缓存。
4.API服务器通知客户端,视频已成功上传且准备好流式传输。
流程b:更新视频
元数据当一个文件被上传到原始存储时,客户端会并行发送一个请求来更新视频元数据,如图-4所示。这个请求包含视频元数据,包括文件名、大小、格式等。API服务器将更新元数据缓存和元数据数据库。
图-4
2.2 视频流式
传输流程用户在YouTube上看视频时,视频总是立即开始被流式传输,用户不需要等到整个视频下载完。“下载完”意味着整个视频被复制到用户的设备上,而“流式传输”意味着用户的设备持续地从远端视频源接收视频流。当用户观看视频时,客户端会逐步加载一小部分数据,这样他们就可以立刻观看视频并可以连续地观看了。在我们讨论视频流式传输流程之前,先了解一个重要的概念:流媒体协议(Streaming Protocol)。这是一个控制视频流式传输的标准方法。常用的流媒体协议有:
•MPEG-DASH。MPEG指的是Moving Picture Experts Group,DASH指的是Dynamic Adaptive Streaming over HTTP。
•Apple HLS。HLS指的是HTTP Live Streaming。
•Microsoft Smooth Streaming.
•Adobe的HDS(HTTP Dynamic Streaming)。
因为这些协议涉及底层细节且需要特定的领域知识,所以你不需要完全理解甚至记住它们的名字。这里重要的是理解不同的流媒体协议支持不同的视频编码和播放器。在设计一个视频流服务时,我们必须选择合适的流媒体协议来支持我们的使用场景。
视频直接从CDN开始流式传输。离用户最近的边缘服务器会传送视频给用户,因此延时非常短。图-5展示了视频流式传输流程的高层级设计。
图-5
3. 深入设计
在高层级设计中,整个系统被分成两个流程:视频上传和视频流式传输。在本节中,我们会通过重要的优化来改进这两个流程,并引入错误处理机制。3.1 视频转码
当你录制视频时,设备(通常是手机或者摄像机)会将视频保存为特定格式的文件。如果你希望录制的视频在其他设备上也可以平滑地播放,就需要将该视频编码成兼容的比特率(bitrate)和格式的视频。比特率是视频中每秒传输的比特数。比特率高通常意味着视频质量高。高比特率的视频流需要系统具备更强的处理能力和更快的网速。视频转码非常重要,原因如下:
•原始视频占用大量的存储空间。时长1小时的高分辨率视频如果按每秒60帧的速度录制的话,会占用几百GB的空间。
•很多设备和浏览器只支持特定类型的视频格式。因此,把视频编码成不同的格式以确保兼容性很重要。
•为了确保用户在观看高质量视频的同时也能维持流畅的播放效果,可以为网络带宽高的用户提供高分辨率的视频,而为网络带宽低的用户提供低分辨率的视频。
•网络状态可能会变化,特别是在移动设备上。为了确保视频可以连续播放,基于网络状况自动切换或者手动切换视频质量,对于顺滑的用户体验来说是必不可少的。有很多类型的编码格式可用。尽管如此,它们多数都包含以下两个部分。
•容器:它像一个包含视频文件、音频和元数据的篮子。你可以通过文件扩展名(比如.avi、.mov或者.mp4)来确定容器格式。
•编解码器(Codec):指的是压缩和解压缩算法,旨在减小视频大小,同时保证视频质量。最常用的视频编解码器是H.264、VP9和HEVC。
3.2 有向无环图模型
视频转码是耗费算力且耗时的任务。此外,不同的内容创作者可能有不同的视频处理需求。比如,有些内容创作者要求在其视频上加水印,有些人自己提供了缩略图,还有些人上传了高分辨率的视频,而其他人则没有这么做。为了支持不同的视频处理流水线和保持高并行性,需要加入一定程度的抽象,让客户端程序员定义要执行的任务。举个例子,Facebook的流式视频引擎使用了有向无环图(Directed Acyclic Graph,DAG)编程模型。该模型定义了不同阶段的任务,使得这些任务可以顺序或者并行执行。在我们的设计里,采用了类似DAG的模型来实现灵活性和并行性。图-6展示了视频转码的DAG模型。
图-6
在图-6中,原始视频被分解为视频(包含实际的视觉图像和内容)、音频(包含原始视频中的声音和音频信息)和元数据这三部分。下面是可以应用到视频文件上的一些任务。
•检查:确保视频有好的质量,没有格式问题。
•视频编码:对视频格式进行转换以支持不同分辨率、编解码器、比特率等。图-7给出了一个例子,展示了编码后的不同文件。
•缩略图:缩略图可以由用户上传,或由系统自动生成。
•水印:在视频上叠加的一个图像,包含视频识别信息。
3.3 视频转码架构
使用了云服务的视频转码架构如图-8所示。该架构有6个主要组成部分:预处理器、DAG调度器、资源管理器、任务Worker、临时存储和作为输出的编码后的视频。接下来,我们仔细看看每个组成部分。图-8
预处理器(见图-9的灰底部分)
图-9
预处理器有4个职责。
1.视频分割。将视频流分割或者进一步分割成更小的图像组(Group of Pictures,GOP),并确保这些GOP在视频流中的位置对齐。GOP是一组/块按特定顺序排列的视频帧。每个块都是可以独立播放的单元,通常其长度为数秒。
2.一些旧的移动设备或者浏览器可能不支持视频分割。预处理器会基于GOP对齐来为旧客户端分割视频。
3.DAG生成。预处理器基于客户端程序员写的配置文件来生成DAG。图-10是一个简化的DAG示例,它有两个节点和一条边。
图-10
这个DAG示例是从两个配置文件中生成的(见图-11)。
图-11
4.缓存数据。预处理器是一种缓存,用于存储分割后的视频。为了提高可靠性,预处理器在临时存储中存储了GOP和元数据。如果视频编码失败,系统可以使用保存的数据来重试。
DAG调度器(见图-12的灰底部分)
图-12
DAG调度器把一个DAG分成不同阶段的任务,并把它们放到资源管理器的任务队列中。图-13展示了DAG调度器是如何工作的。
图-13
如图-13所示,原始视频的转码被分成了两个阶段。阶段1处理视频、音频和元数据。对视频的处理在阶段2被进一步分成两个任务:视频编码和缩略图。在阶段2中,还需要对音频文件进行音频编码。
资源管理器(见图-14的灰底部分)
图-14
资源管理器负责管理资源分配的效率。它包含三个队列和一个任务调度器,如图-15所示。
•任务队列:包含待执行任务的优先级队列。
•Worker队列:包含Worker使用信息的优先级队列。
•运行队列:包含与当前正在运行的任务和执行这些任务的Worker有关的信息。
•任务调度器:它选取最合适的任务与Worker,并指示选中的Worker来执行任务。
图-15
任务调度器的工作内容如下:
•从任务队列里获取优先级最高的任务。
•从Worker队列里获取最合适的Worker。
•指示选中的Worker来执行任务。
•将任务与Worker信息绑定,并把它们放到运行队列里。
•一旦任务完成,任务调度器就从运行队列里移除该任务。
任务Worker(见图-16的灰底部分)
图-16
任务Worker执行在DAG中定义的任务。不同的任务Worker可能会执行不同的任务,如图-17所示。
图-17
临时存储(见图-18的灰底部分)
这里用到了多个存储系统。选择使用哪个存储系统取决于数据类型、数据大小、访问频率、数据生命周期等因素。举个例子,元数据会被Worker频繁访问,而元数据通常很小,因此,把元数据缓存在内存中是个好主意。对于视频或者音频数据,我们把它们放在Blob存储中。在对应的视频被处理完以后,临时存储中的数据就会被清除。
图-18
编码后的视频(见图-19的灰底部分)
图-19
编码后的视频是编码流水线的最后输出。比如,输出为文件funny_720p.mp4。
3.4 系统优化
现在,你应该了解了视频上传流程、视频流式传输流程和视频转码。接下来,我们会通过一些优化措施来完善系统,包括提升速度、提高安全性和节省开销。速度优化措施1:并行上传视频
将视频作为一个整体上传,效率不高。我们可以通过GOP对齐把视频分成小块,如图-20所示。
图-20
这可以让我们在视频上传失败时快速恢复上传。按照GOP对齐来分割视频的工作可以在客户端执行,以提升上传速度,如图-21所示。
图-21
速度优化措施2:把上传中心安置在离用户近的地方
另一种提升上传速度的方法是在全球设立多个上传中心。美国的用户可以将视频上传到北美上传中心,中国的用户可以将视频上传到亚洲上传中心。为此,我们使用CDN来作为上传中心。
速度优化措施3:每一处都并行
为了达到低延时,需要付出很大努力。还有一个优化措施是构建一个松耦合的系统以实现高并行性。我们需要对之前的设计做一些修改来实现高并行性。
我们先仔细看一下视频从原始存储传到CDN的流程。如图-22所示,这个流程展示了所有的输出都取决于上一步的输入。这种依赖关系使并行变得很困难。
图-22
为了让系统各部分之间的耦合更松散,我们引入了消息队列(见图-23)。我们用一个例子来解释消息队列是如何让系统变成松耦合的。
•在引入消息队列之前,编码模块必须等待下载模块的输出。
•在引入消息队列之后,编码模块再也不需要等待下载模块的输出。如果在消息队列中有事件(编码任务),编码模块可以并行地处理这些编码任务。
图-23
安全性优化措施1:预签名URL
对于任何产品,安全都是最重要的一个方面。为了确保只有获得授权的用户才可以将视频上传到正确的地址,我们引入了如图-24所示的预签名URL。
图-24
上传流程的变化如下:
1.客户端向API服务器发送HTTP请求来获取预签名URL,从而获取预签名URL中所标识的对象的访问权限。将文件上传到Amazon S3时会使用“预签名URL”这个术语。别的云服务提供商可能使用的是不同的说法。比如,微软的Azure Blob存储支持同样的功能,但是其名字为“共享访问签名”(Shared Access Signature)。
2.API服务器返回一个预签名URL。
3.一旦客户端收到响应,就使用这个预签名URL来上传视频。
安全性优化措施2:保护有版权的视频
很多内容制作者不想把视频发布到网上,因为他们害怕自己的原创视频会被盗用。为了保护有版权的视频,我们可以采用下面3个安全性选项。
•数字版权管理(Digital Rights Management,DRM)系统:Apple FairPlay、Google Widevine和Microsoft PlayReady是3个主要的DRM系统。
•AES加密:你可以加密视频并配置身份验证策略。加密视频会在播放时被解密。这确保了只有授权用户才可以观看加密视频。
•可视水印:这是一个浮在视频上的图像,包含视频的标识信息。它可以是公司的logo或者公司名。
节省开销的措施
CDN是我们系统中的关键组件,它确保了视频在全球范围内快速传输。但是,基于封底估算,我们知道CDN很贵,特别是当数据量很大时。怎样才能减少开销呢?之前的研究表明,YouTube视频流遵循长尾分布(Long-Tail Distribution),。这意味着少数热门视频会被频繁地播放,而很多其他的视频只有很少的观众或者没有人看。基于这个发现,我们可以实施如下优化措施。1.仅经由CDN提供最流行的视频,而其他视频则由我们的大容量视频服务器提供(图-25)。
图-25
2.对于不那么流行的视频,我们可能不需要存储多个编码过的视频版本。对短视频可以按需编码。
3.一些视频只在特定地区流行,没有必要把这些视频分发到其他地区。
4.像Netflix一样构建自己的CDN并和ISP(Internet Service Provider,互联网服务提供商)建立合作关系。构建自己的CDN是个巨大的项目,但对于大型流媒体公司来说,这种做法很有意义。ISP可以是Comcast、AT&T、Verizon或者其他互联网服务提供商。ISP遍布全球,靠近用户。和ISP合作可以提升用户的观看体验并减少带宽费用。
所有优化都要基于视频内容的流行度、用户访问模式、视频大小等因素进行。在做优化之前分析用户的历史观看模式很重要。
3.5 错误处理
对于一个大型系统,系统错误是无法避免的。为了创建一个高度容错的系统,我们必须优雅地处理错误并从错误中快速恢复。一般来说,像Youtube这种视频分享系统存在以下两种类型的错误。•可恢复错误。对于可恢复错误,如视频分块转码失败,一般的做法是将操作重试几次。如果任务持续失败,并且系统认为不可恢复,就会返回一个合适的错误码给客户端。
•不可恢复错误。对于不可恢复错误,比如视频格式有问题,系统会停止与视频有关的正在运行的任务,并返回合适的错误码给客户端。以下是各个系统组件的典型错误和应对方案。
•上传错误:重试几次。
•视频分割错误:如果老版本的客户端无法按照GOP对齐的方式来分割视频,就把整个视频传给服务器,在服务器端完成分割视频的工作。•转码错误:重试。
•预处理器错误:重新生成DAG。
•DAG调度器错误:重新调度任务。
•资源管理器队列不可用:使用副本。
•任务Worker不可用:在新Worker上重试任务。
•API服务器不可用:API服务器是无状态的,所以请求会被重定向到另一个API服务器。
•元数据缓存服务器不可用:将数据复制多次。如果一个节点不可用,你依然可以访问其他节点来获取数据。我们可以启用一个新的缓存服务器来替换宕机的那个。
•元数据数据库不可用。
◆ 主库不可用。如果主库不可用,就推举一个从库来做新的主库。
◆ 从库不可用。如果从库不可用,可以使用另一个从库来做读操作,再启用一个数据库服务器来替换宕机的那个。
4. 总结
在本章中,我们展示了类似YouTube的视频流媒体服务的架构设计。如果在面试的最后还有多余的时间,可以讨论下面的几个话题。•扩展API层:因为API服务器是无状态的,所以可以很容易地横向扩展API层。•扩展数据库:你可以谈论数据库复制和分片。
•直播流媒体:指的是实时录制和广播视频。尽管我们的系统不是专门设计来进行直播的,但是直播和非直播流媒体有一些相似点,比如都需要对视频进行上传、编码和流式传输等操作。直播和非直播流媒体的显著区别有:◆ 直播有更高的延时要求,所以它可能需要使用不同的流媒体协议。
◆ 直播有更低的并行要求,因为小块的数据已经被实时处理了。
◆ 直播需要采用不同的错误处理方法。任何要耗时的错误处理方法都是不可接受的。
•视频下架:所有侵犯版权、包含色情内容或者存在其他非法行为的视频应该被移除。这类视频中有一些在上传过程中就可以被系统发现,而其他的则可能要通过用户标记来发现。