在尼恩的(50+)读者社群中,经常指导大家面试架构,拿高端offer。
前几天,指导一个年薪100W小伙伴,拿到字节面试邀请。
遇到一个 非常、非常高频的一个面试题,但是很不好回答,类似如下:
- 短视频系统,如何做系统架构?
- 短视频APP,如何做系统架构?
最近,有个网易二面,又遇到了这个问题。
其实,尼恩一直想梳理一个教科书式的答案,
这里有一个新的行业案例《字节跳动亿级视频处理系统高可用架构实践》,尼恩从 面试维度,对这个方案,进行二次重构和梳理,现在把其做为参考答案,收入咱们的《尼恩Java面试宝典 PDF》 V97版本
下面的内容,是尼恩是结合自己的3高架构笔记,以及尼恩的3高架构知识体系(3高架构宇宙)做的二次分析。
《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请到公号【技术自由圈】取
以短视频点播为代表的流媒体技术应用在移动互联网时代实现了快速扩张。
现在,短视频内容已成为新趋势,每个人都在从TikTok、Instagram、YouTube等平台上消费这些内容。让我们看看如何为TikTok创建一个系统。
在互联网内容趋于多元化的今天,短视频迅速替代了传统的文字图片,席卷了人们的视野和生活,成为信息传播的重要渠道。
这样的应用程序看起来很小,但在后台有很多事情正在进行。
以下是相关的挑战:
让我们逐一了解每个部分。我将其分为三个部分:
这是一个包含与用户相关服务的服务,如下所示:
为了存储与用户相关的数据,我们将使用基于SQL的数据库,如MYSQL或PostgreSQL,因为与用户相关的数据(例如追踪关注者)将会是关联数据,所以这是一个适当的选择。
为了优化数据库性能,我们将使用主从架构。主数据库用于执行写操作,从数据库用于执行读操作。要了解有关此内容的更多信息,可以阅读文章如何优化数据库性能并扩展它?[3]
现在让我们讨论用户服务的流程。应用程序将发出API调用,API Gateway将管理这些API。
它将为用户服务路由请求。
请求将通过负载均衡器进行,负载均衡器下将有多个用户服务实例。
根据负载,它将决定哪个实例将处理请求。
一旦请求被处理,负载均衡器将将响应发送回API网关,然后再发送回应用程序。
一般包含视频上传、存储、处理、播放等流程及相应的流程管理与审核。
核心的操作如下所示:
与视频发布相关的子系统的技术要点:
核心的核心,就是短视频的存储。
为了存储与帖子相关的数据,我们将使用基于NoSQL的数据库,如MiniO。
对于每个用户,可能会有成千上万的帖子,这将导致大量数据。
为了实现最佳性能,扩展数据库可能会很困难。NoSQL数据库支持水平分片,这有助于我们在不影响性能的情况下扩展数据库。
现在让我们讨论视频服务的流程。
应用程序将发出API调用,API Gateway将管理这些API。它将为视频服务路由请求。
请求将通过负载均衡器进行,负载均衡器下将有多个视频服务实例。
根据负载,它将决定哪个实例将处理请求。一旦请求被处理,负载均衡器将将响应发送回API网关,然后再发送回应用程序。
视频文件将上传到NOSQL,如MiniO。
现在,如果我们想在世界范围内任何地方访问文件而没有任何延迟,那么该文件将发送到内容分发网络(CDN),它将将媒体文件更新到世界各地的不同数据云存储中。
还有一个挑战需要解决,即原始视频的大小可能较大,因此如果将大文件发送回客户端,则下载时间会更长,这会影响用户体验。
文件一旦上传到云存储,您可以在数据库中存储文件路径。
然后将帖子/视频详细信息发送到消息队列系统,如Kafka或RockerMq。
为了使用户体验流畅,我们需要压缩视频并为不同设备创建不同分辨率的视频。
视频处理工作者将从消息队列系统接收视频详细信息,然后从
云存储中提取文件并进行处理。处理完成后,这些新的视频文件将发送到CDN。
现在您可能会想,应用程序如何知道上述讨论中压缩的视频的文件路径?由于压缩文件将存储在分类文件夹中,因此可以根据分辨率和文件名轻松查找文件。
视频发布API只会返回文件名,而要访问文件,应用程序将在URL本身中添加分辨率细节,例如/media//mediaID/xxxx
。
当访问此URL时,它将经过API网关,并从URL中提取分辨率和文件名详细信息。
然后它将在缓存系统(Redis)中检查,如果文件不可用,则将访问CDN并通过它获取文件。然后将其添加到缓存中,以便如果再次请求相同文件,则不必从CDN获取。
这是一个包含与视频点赞和评论相关服务的服务。正如名称所示,通过此服务,我们可以为特定帖子更新点赞和评论。与上面讨论的其他流程相同。
通过此服务,基于用户偏好推荐一系列帖子。幕后有很多其他事情正在进行。让我们看看幕后运行的流程。
然后,创建一个帖子后,它将被发送到消息队列系统,然后消费者将提取数据并将数据更新到**大数据(Hadoop)**中。
将为机器学习服务(如PyTorch和Tensorflow)设置单独的服务器,在这里它将从大数据中提取数据并训练模型。
推荐服务将使用此AI模型为给定用户推荐帖子。
当前存储从逻辑上一般可分为三类,即块存储、文件存储和对象存储。
业内较为主流的开源存储框架MinIO、Ceph、SeaweedFS,从开源协议、扩展性、成本等多方面进行对比如下表:
由于对象存储结合了块存储读写效率快、存储空间可扩展以及文件存储方便共享的优点,同时结合短视频平台数据存储与视频点播需求,建议选取对象存储框架作为短视频点播平台的存储逻辑。
进一步考虑到短视频点播平台数据规模、存储动态不宕机扩容、在线HTTP多媒体播放以及学习运维成本等需求,通过以上对比,建议选用MinIO开源框架作为短视频存储与点播基础框架。
对象存储的出现是为解决了存储海量大数据的问题,如存储海量的视频、图片,并进行数据归档、数据备份、大数据分析等操作。
对象存储一般采用key-object的扁平化存储架构,使用方便,调用API就可进行数据的多样化读写。其大容量、动态扩展、数据灾备等性能,是传统文件存储和NAS无法比拟的。
MinIO是一套基于Apache License V2.0协议的轻量级、高性能开源对象存储框架,适用于图片、视频、镜像等海量非结构化数据存储。
MinIO采用Golang实现,客户端支持Java、Python、JavaScript、Golang语言,兼容亚马逊S3云存储服务接口,方便与其他应用结合。
1)存储机制
MinIO使用纠删码(erasure code)和校验和(checksum)来保护数据免受硬件故障和无声数据损坏,可保证N/2节点损坏的情况下数据的正常访问。
2)扩展性
极简性和扩展性是MinIO集群的两个重要设计理念。
MinIO支持对等扩容和联邦扩容两种扩容方式。对等扩容,即通过增加对等的集群节点和磁盘以扩充集群,例如,原集群包含4个节点4块磁盘,则扩容时可同样增加4个节点4个磁盘(或其倍数),以此保证系统可维持相同的数据冗余SLA,降低扩展的复杂性。
联邦扩容,其基本原理是引入etcd作为统一命名空间,将多个MinIO集群组成一个联邦,可在原有联邦的基础上,向联邦中增加新集群,此扩容方式理论可实现无限扩展,且可以实现在原联邦服务不中断的情况下扩容。
3)对外服务
MinIO完全兼容S3标准接口,客户端和服务端之间通过http/https进行通信。MinIO提供客户端mc(MinIO Client)以支持UNIX命令,同时支持多语言的客户端SDK。此外,其存储后端除使用磁盘外,还可通过网关对接其他存储系统与资源。具体如下表所示。
4)多媒体拉流支持
MinIO对于多媒体文件,支持HTTP-Range的方式在线拉流播放与音视频进度条拖拽。
如下图,使用浏览器以流的形式访问存储于MinIO的多媒体文件时,每拖动一次进度条,则会向MinIO服务端发送一条Http-Request请求,请求Headers中包含Range字段,其内容是当前请求视频进度的开始字节数及缓存结束字节数。
这种形式使MinIO天生支持多媒体文件的拉流播放与进度拖拽。
图 MinIO多媒体在线播放支持
出于集群存储可动态扩展性、支持HTTP流式播放、运营成本等因素,
建议使用MinIO对象存储作为底层存储,开发部署短视频点播地址映射、地址动态代理等服务,实现一套短视频存储点播平台。
其实现框架如下图:
图 基于MinIO的短视频点播平台架构
点播平台大致可分为存储层、服务层与应用层。
基于MinIO对象存储的点播平台数据访问流程如下图所示:
图 基于MinIO的短视频点播平台数据访问流程图
统一采用mp4格式作为视频存储和点播格式,为了兼容多种格式视频文件上传,需开发转码模块将其转码成mp4格式进行存储,将其首先存入本地磁盘缓存。
在直播的过程中开启录制,将录制的文件首先存入本地磁盘缓存。
视频转码完成或录制完成后,调用MinIO文件上传接口,将视频文件上传至MinIO集群/联邦,由etcd对MinIO集群提供注册发现服务。
服务端部署点播地址映射服务模块,实现MinIO视频点播地址与视频ID的映射,使存储介质的改变不影响视频点播拉流。
出于系统安全性考虑,我们不希望暴露MinIO存储地址与存储细节,希望增加一层网关进行媒体流的转发或地址的代理,对外提供统一的服务地址,使用地址动态代理,可以根据点播请求的视频ID不同,动态代理至不同的视频播放地址,实现视频存储细节与服务地址的解耦。
客户端或浏览器使用HTTP协议流式拉取视频文件并播放。
选用MinIO开源存储框架,快速设计并搭建出一套支持海量短视频上传、存储、点播等功能的视频点播平台,
为当下不断涌现的短视频点播平台及相关应用提供了一定技术选型与设计参考。
除此minio存储之外,短视频对CDN分发也是有很高要求的,
跟传统的长视频相比的话,因为长视频会进行预取刷新的操作,会预先将文件分发到CDN节点上去,
但是短视频内容因为是UGC,而且视频上传完成之后页面马上就要发布出去,进行播放,所以往往不能像长视频那样,提前预取到各个CDN节点,进行预热,这对视频云平台内部的分发能力是有要求的。
用户拍完一段视频,需要立即上传。
CDN厂商一般全国各地有多个数据中心,“从基础资源能力上来讲,要求CDN网络有条件为客户提供就近上传的功能”。
如何实现?
通过一套SDK,开发者将这套SDK嵌入到他们APP里面去,最终用户在将视频上传的时候,会通过HTTP DNS的调度去获取离他最近的或者是当前网络中最佳的一个数据中心节点,并且实现这个文件的上传功能。
字节跳动火山引擎视频中台支撑了多个亿级应用的视频全生命周期管理:
全部视频生命周期:
视频整体的生命周期大致可以分为四个阶段:
在视频生命周期里,视频处理系统是云端生产的核心环节。
我们先来看一下字节跳动在视频处理这方面所面临的一些挑战。
面临以上这些挑战,视频处理系统要实现哪些目标呢?
大家可以看上图,这张图更偏逻辑的关系。视频处理系统最终的目标总结起来只有三点:
为了实现这些目标,就需要对视频做不同类型的处理,包括转码、编辑、分析,也包括一些图片处理,每一项都是一种视频的应用。
每一个视频的应用再往下拆解会对应非常多的处理能力,比如对于转码应用来说,会有一些新的编码器、自适应转码来降低码率;通过一些增强的方式提高画质等等。
所有这些能力在最底层都由一个基础的处理系统做支撑。这个系统需要满足高可用、高可扩展以及高效的运维开发效率的需求。
总结起来就是,整个视频处理系统以底层的系统支撑为基础,构建各种各样的视频处理的能力,形成多种视频应用,从而满足业务场景的需要,提升体验,降低成本。
为了实现这些目标,视频处理系统的架构如上图所示,外层被划分为三个平面:
中间的四层分别是:
下面将为大家详细介绍几个核心层。
服务层有几个重要的组件。
服务层下方就是媒体工作流的引擎,工作流是以 DAG 的形式组织一系列视频处理的流程。
比如说在西瓜视频上传一个视频后,需要去抽取它的封面,并对视频进行无水印转码,还需要进行各种档位的转码。
这些都是处理视频的流程,每一个流程都是一个细粒度的任务。
有效的办法是:把这些单个的流程组织起来就形成了一个工作流。
工作流能解决什么问题呢?
下面再简单看一下工作流内部是怎样的结构。
工作流内部主要包含这么几个模块:
上图中间绿色的部分就是整个工作流的引擎。
上层就是服务层,下层是等会要介绍的函数计算平台。
视频处理系统是一个离线处理系统,每一个任务都会执行几十秒、几分钟甚至更长的时间。
这时最重要是保证过来的每一个任务都能够最终被执行,而且最终保持一致。
所以对系统来讲,需要有 at least once 的保证。
其次是任务幂等的要求。
任务幂等有两个意思:
为了保证任务幂等,我们在视频 meta 信息关联以及视频的存储方面都做了比较多的工作,同时要保证 at least once,在工作流和节点层面都做了比较多的超时检测和重试的机制。
视频处理系统的下游涉及到计算资源和存储资源。
一旦计算资源和存储资源出问题,很难有一个完美的方案对上层业务做到无感知,要做的就是尽量避免损失,尽量减少对于业务的影响。
这里面有两个比较重要的措施:
多级限流:限流是常用的手段,但是视频处理的不同点是会有一个任务筛选的过程,需要保证在有限的资源里,所有重要的任务要被优先执行。
举个例子,假设底层计算资源现在突然变为正常的一半,如何减少对业务的影响?
首先,在工作流层面,需要把一些对任务延时不敏感的工作流任务进行 delay,这就需要一些策略的预设置;
另外,同一个工作流里面,需要对不同的节点进行优先级的配置,比如视频要转出五个档位,可能其中有两个档位是大家消费概率最高的,就需要把这两个档位优先转出来,其他的档位进行延迟处理。这整体就涉及到了多级限流以及限流策略配置的一种能力。
批量重转:它是什么意思呢?举个例子,假设昨天底层的同学上线了一个有问题的功能,但是今天才发现。这时要做的是把昨天这个功能上线这个功能以后所影响的视频全部筛选出来,快速进行重转,而且不能够影响目前正在运行的业务。
这里面涉及到两个问题。
第一点是如何准确的从某任意一个时间点到另外一个时间点把这一批视频全部都挑出来。
第二点是快速重传,而且不能影响线上的业务。所以这里需要有一个单独的子系统来负责整体的批量任务的查找和重转。
从系统维度我们也做了一些工作,包括中间件的冗余备份、对下游异常的检测等。当发现某些实例有问题,会对这些实例进行熔断和摘除。其次,系统有比较完善的流量切换方案,因为系统经过多次大型活动的考验,也具有完备的压测及预案,这些对于系统的高可用也非常关键。
上面介绍了工作流的系统。下面介绍一下它的下层函数计算平台。
首先介绍一下函数的概念。
函数对应于媒体工作流里的一个节点,同时,它也对应于一个细粒度的视频处理的任务。更通俗一点,它对应一段可以执行的程序。
这个函数计算平台需要提供哪些能力呢?
上图是这个函数计算平台的基本架构。
图中左侧部分是一个控制平面,开发者可以开发一个函数,通过管理 UI 注册到函数计算平台上。
图中右边是整个函数调用的流程。这个流程首先会经过该函数计算平台的一个 gateway,到集群层面的调度,然后会到一个单集群里,单集群内部是我们自研的一个中心调度系统 Order。
Order 有一个中心调度器,会把这个任务调度到一个具体的节点上去执行。这个节点就会拉取整个函数的可执行的包,然后执行这个函数。
在多集群层面,
上图是简单的多机房示意图。
图中左右两边都是一个机房,每一个机房里有多个集群。
每个机房里会有一个集群级别的调度器模块,多机房之间又会有一个模块,这个模块负责同步各个机房的资源情况,包括资源的总量和使用情况等。
我们的单集群是一个中心调度系统,有一个中心调度器,称为 Server;会有一个执行单元,称为 Client。Server 是多实例、无状态的,能够平滑、动态地升级。
在 Server、 Client 之间会有状态检测,以及问题节点的熔断和任务重试等机制。
一般情况下,Server 会通过心跳判断一个节点是否活着。除了这一点,Server 也会观测节点整体的状态。
比如一个任务是否超时比较多,或者任务的失败率比较高等,当发生这种情况的时候,也会对节点执行熔断策略。
前面介绍过函数计算平台分了几层,从上层的网关层,到集群的调度,到机器内部的调度。
每一层都是一个多实例的服务。所以,每个上游都会对下游有一些异常检测+摘除,也就是所有的组件都会有单点的异常处理。此外还有一些中间件熔断的策略。
BMF 全称是 ByteDance Media Framework。它是字节跳动自研的多媒体处理框架。
之所以会去自研一个视频处理框架,是因为我们发现传统的一些视频处理框架会有一些限制。
如果这个框架有很多人在开发和维护,那么它的依赖越来越多,最后会大大降低开发和运维的效率。
为了解决上面这些问题,我们自研了 BMF 多媒体处理框架。它的目标包括:
上图是 BMF 框架的整体架构。
最上层是应用层。应用层上的每一块是一个视频应用,比如前面提到的的视频转码、视频编辑、图片处理等等。下层是模块层,每一个模块是视频处理的一个细粒度的原子能力。例如对视频进行编解码,或者是进行 ROI 检测等等。
应用层和模块层是通过中间的框架层串联起来的。
这个框架层的中心是一个核心的引擎。这个引擎对上提供一套比较通用、简洁的流式接口,使开发者能够比较容易地搭建出视频处理的应用。该接口支持多语言,包括 C++、Python、Go。对下,它会提供一套比较完善的模块开发的 SDK,也支持 C++、Python、Go 这几种语言。
围绕着核心引擎,我们又做了一些相关的服务和工具集,这个服务主要用来管理模块的版本、依赖等。
这样的架构有一个最大的好处,就是把开发者做了一个比较好的划分。
不同模块的开发者可以只专注于自己模块的开发,选用自己熟悉的语言。
模块开发完以后,整个模块就可以注册到系统里去。上层的应用开发是支持业务的,业务可以不用了解底层的模块是怎么实现的,也不用知道这个模块是什么语言实现的,只要借助这个框架提供的接口,就可以对这个模块进行无缝串联和使用。
上图进一步介绍了 BMF 的动态开发模式。
举个例子,在实际情况当中,算法开发人员研发了视频处理的算法。
首先这个算法会送到算法优化同学那里做优化。优化完以后,整个算法就会形成一个模型。
接下来算法优化的同学会把这个模型注册到系统上,模块的开发同学会把这个模型封装成具体的模块,也是注册到系统里面去,这个模块就是一个具体的原子的能力。
再接下去,函数开发者,也就是业务开发同学,就可以把模块串联成一个具体的视频处理应用,做成一个函数注册到函数管理平台,然后灰度上线。
大家可以看到整个流程里面的各个团队分工都非常明确,独立地开发协作效率很高。
而且这个流程里面所有模块的原子能力都是可以复用的。流程也不会牵扯到任何编译的依赖,全部都是动态进行的。
上图是视频转码的完整流程示例。当用户上传一个视频以后,这个视频首先会进入服务端的存储,这时会触发一个转码的流程,也就是提交一个工作流任务,这个任务首先会经过转码的服务,然后被放到弹性队列里去;下一步,任务从弹性队列出队,进入到工作流引擎里面去执行;工作流引擎会把工作流拆解为一个个细粒度的任务,然后送到函数计算平台去执行,每一个函数,会采用前面介绍的 BMF 动态开发的方式去构建。最终,当所有细粒度节点的任务都执行完,整个工作流也完成以后,转码或者视频处理的流程就完成,再一步步往上返回。
最后,再回顾一下本文的一些要点:
首先,视频处理系统,最重要的几点要求包括:高可用,也就是系统的稳定性;高可扩展性,当支持非常多的业务场景的时候,系统的可扩展性对整体高可用也会有很大的影响;开发以及运维的效率。
整体的架构,总结起来就是媒体工作流加上函数计算平台,加上动态的多媒体框架 BMF 这三个核心的部分。高可用性方面,在服务层会有任务幂等、多级限流以及批量重传。在平台层会有多机房、多集群的切流策略,单集群内部的冗余、上下游的异常检测等。最后,底层的动态多媒体框架,它虽然没有直接提升系统的高可用性,但是它提升了系统的可扩展性,以及开发和运维的效率,所以它对于系统也起到了非常重要的作用。
未来,系统会往更智能化的方向去发展。我们希望能构建一种分布式调度的执行平台,用户只需要关注处理流程,流程的拆分、资源调度,如何执行都由平台来决定。
https://blog.csdn.net/weixin_37604985/article/details/132179317
https://zhuanlan.zhihu.com/p/381259391
https://blog.csdn.net/csdnnews/article/details/117915142
结合 字节的方案,大家回到前面的面试题:
以上的方案,才是完美的答案,才是“教科书式” 答案。
后续,尼恩会给大家结合行业案例,分析出更多,更加劲爆的答案。
当然,如果遇到这类问题,可以找尼恩求助。
为了大家拿高端offer,拿架构offer,即将发布:
并且提供配套的简历模板, 帮助大家进行简历的亮点重建和升级,最终帮助大家进大厂、做架构、拿高薪。
《炸裂,靠“吹牛”过京东一面,月薪40K》
《太猛了,靠“吹牛”过顺丰一面,月薪30K》
《炸裂了…京东一面索命40问,过了就50W+》
《问麻了…阿里一面索命27问,过了就60W+》
《百度狂问3小时,大厂offer到手,小伙真狠!》
《饿了么太狠:面个高级Java,抖这多硬活、狠活》
《字节狂问一小时,小伙offer到手,太狠了!》
《收个滴滴Offer:从小伙三面经历,看看需要学点啥?》
《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》PDF,请到下面公号【技术自由圈】取↓↓↓