Up主上传的大量优质视频内容使得bilibili(B站)深受年轻用户的喜爱。bilibili视频云高级研发经理 唐君行在LiveVideoStack线上交流分享中详细介绍了B站为提供更流畅、稳定用户体验,努力优化上传系统架构,建立质量体系以及质量调优中的实践经验。
文 / Json(唐君行)
整理 / LiveVideoStack
直播回放:
https://www.baijiayun.com/web/playback/index?classid=19012252228433&token=oxx9-k_ZrxvhKKZLIMQ5hscti3kMCPTn5jjVy0_0ZBG_6qrsbCBq-khAVqheOz1TCqdH1zJ1Si0
大家好,我是来自bilibili(B站)的Json,目前主要负责bilibili的视频上传、存储、点播CDN建设与架构演进。在进入B站之前我主要负责系统架构设计与数据驱动研发,同样这也是我非常擅长的领域。
我将从以上几个方面为大家分享B站上传调优实践的具体内容:两三年前,B站服务器有限的承载能力使得用户上传视频失败的状况时有发生,这也是我刚加入B站时希望通过调整系统架构努力优化的方面;同时这样一个基于数据优化的项目,需要建立正确的优化指标与质量优化方案,借助数据的力量驱动整个平台的开发,使得用户能够在B站收获流畅稳定的使用体验。
1. 系统架构
上图展示的是B站的用户创作中心,用户上传到B站的视频文件体积较大,质量较高。初期我们仅开发了通过PC(网页端)上传文件的功能。视频体积较大是我们的优化工作带中的主要挑战,因为只有所有分片都上传成功,一个视频文件才算被上传成功。
B站为了确保内容的高质量,仅有答对一定数量题目的Up主才能获取上传视频的资格,这就使得上传至B站的视频内容多为平均体积在400MB左右的优质长视频;虽然采取了这种设置一定门槛的上传机制,但我们的日上传量依旧保持在15万左右并且维持着年100%的增长,这也就是为什么在未来我们希望在巩固现有网页版上传服务的同时继续开发Android、iOS、PC客户端的上传功能,方便用户随时随地上传高质量视频内容。
作为上传系统的关键组件,存储部分的架构并不复杂。我们使用一台节点服务器处理数据提供服务,当用户将视频文件成功上传至此服务器时,视频文件在服务器后端会经过一系列系统处理,并在主机之间多次交换数据,其传输协议与沟通方式更像一个运维系统,比较原始,我们将其重构为一个带有网关的系统,改进原先每开始一种新业务就需新分配一批服务器的机制,使用网关的汇集功能可大大降低在配置域名与接入新业务等运维工作的压力与成本,并将节省下来的时间资源运用于优化工作。
除此之外,我们将统一存储升级为可以分Bucket配置不同参数且与业务无关的通用对象存储,其承接了B站的UGC、PGC、音频、短视频的上传、处理与回源等工作;同时,使用此上传、存储体系统一的方案可有效减少文件在系统间转移产生的IO。
随着B站的不断发展,从一开始投入大量资源至业务发展到现在逐渐专注于内容与资源的分享与呈现,B站不断尝试将主要精力从与业务相关的内容剥离出并投入优化工作,基于此网关统一所有业务的通讯协议从而进一步简化存储架构。
我们基于OpenResty搭建了B站的上传系统。初期B站的上传系统基于PHP搭建,虽然PHP并没有明显缺陷,但由于其还需搭建Nginx才能正常工作,因此相对于OpenResty这样可直接在Nginx中读取与合并上传文件分片的架构,PHP并不是我们的首选项。使用OpenResty的同时,对外发起Gap请求的次数也更多,同样也支持用户从网站获取视频数据的高性能模式。纯粹从编程语言层面来讲 ,以上是OpenResty在网关层面的作用之一。就我目前的体验来看,OpenResty并不输给其他语言:我们的视频云中包括大量基于OpenResty架构的技术栈,而CDN、存储系统与直播系统也基于OpenResty实现,推荐大家使用OpenResty。
一般大公司会使用OpenResty重写相应模块以应对高并发场景,实际来说我们可在一些高I/O场景中使用OpenResty。虽然Node.js与Go也可实现异步I/O但其需要开发者编写大量的回调,而OpenResty能够借助同步代码实现异步I/O;相对于需要大量时间精力应对高并发场景的Python与PHP,OpenResty可极大提高开发效率。除此之外,Nginx本身也扮演着网关与业务服务的角色,可明显简化架构;OpenResty也可直接访问Nginx的ClientBody且不存在语言框架的损耗,如果是其他语言则需在CGI层通过上行Buffer将ClientBody传至所用语言中,并且需要一定转化如Python需要WSGI转化;最后,Lua较高的开发效率也能帮助开发者尽可能实现最佳开发效果。
上图展示了存储系统的整体架构,其主要分为以下三个模块:网关、Meta数据库与存储。用户的访问请求首先经由网关处理,网关继而读取Meta数据库中一部分符合请求存储位置的数据,随后存储会将网络流依次通过图中的路径5与路径6传输至客户端。这样一个简单的存储架构自然而然会受到许多需要重构小部分存储减小内容存储风险的公司的青睐。
我们选择S3作为上传协议。在早期上传协议的选型过程中我们研究了国内许多互联网公司的选型策略,其实国内的大部分互联网公司所采取的上传协议都类似于亚马逊的S3协议,因此我们在构建B站的上传存储系统时直接使用了亚马逊的这一协议,其优势不仅在于亚马逊的上传协议本身就很完善,更在于我们可直接复用亚马逊S3生态中的大量组件。无论是在分片上传、分片大小、并行数量还是在其他我们能想到的可用于关键技术上的优化,通过长期的实践与探索亚马逊已将其一一实现,不得不说这明显降低了我们的开发难度与工作压力。
S3上传协议的整体流程如上图展示:首先,初始化过程会在服务端为用户分配一定空间,随后来自客户端的数据会通过并行上传发送至服务端;在这里我们考虑优化整个上传过程,为提升上传速度与上传体验我们使用并行上传的方式分片传输数据;在这些数据被存储于服务端时,客户端会生成与并行传输相关的会话,此会话会被服务端读取从而将所有分片正确合并。
S3上传协议共需三个请求:用于分配上传空间的Uploads、用于并行上传的Put与用于合并完整文件的Post-complete。其中Put用于并行上传,并行上传时分片大小可自己定义,分片的顺序则通过PartNumber标示;在合并文件时服务端根据PartNumber所标示的不同顺序正确组合所有分片合并生成完整文件并在最后进行校验从而确保合并完整文件的正确无误。
上图展示的便是整个上传系统与后续工作的简化架构。首先,客户端会请求上传调度,其实如果按照中心存储架构重构来说上传调度并非必要流程,这里设计上传调度的目的是便于后续优化工作的进行:上传调度可为客户端分配用于将数据上传至CDN的不同地址与不同上传模式。紧接着在客户端请求完成调度并将数据上传至存储之后,存储会产生一条可驱动视频处理流程的消息,从而启动转码、截图、审核、分发等一系列围绕存储进行的流程。与此同时,存储会接受所有上传分片并产生相应日志,这些日志会被传输至日志中心并呈现给负责分析的运维人员或作为可优化上传调度过程提升用户体验的信息使用。
2. 建立指标
拥有上传架构之后,我们需要建立完善的指标体系。音视频行业有与播放相关的首帧、卡顿率等用于表征用户播放体验的指标参数。我们可凭借这些指标反映出的动态变化规律对用户的多方面体验做出优化。对于上传过程而言,大家比较容易想到的与用户上传体验相关的指标,首先就是分片上传的瞬时速度——瞬时速度决定数据上传的耗时,耗时越短用户体验越优秀;但瞬时速度的计算非常复杂,且无法明确帮助我们实现期待的优化目标。慢速比也是十分关键的指标之一,但慢速比不但不满足线性相关关系,同样难以帮助我们实现优化目标。瞬时速度与慢速比并不能很好体现用户上传体验的优劣,我们希望建立一些能够帮助我们实现优化目标的指标,且计算过程相对简单,允许我们通过数据尽可能详细量化优化所带来的用户体验的变动。经过不断探索我们将全局均速与成功率作为评价用户上传体验的两项关键指标——全局均速的定义是使用一个用户所上传文件的体积除以上传时间得到的一个以兆每秒为单位的物理量,成功率则是用户完成上传的次数除以用户发起上传的次数,而后将结果乘以100%。通过长时间的实践,我们可以说此两项指标能够客观准确地反映出用户上传体验的优劣程度。
数据闭环值得我们在优化过程中重点关注。将用户反馈与上述指标体系中的关键数据相结合,我们便能得到有助于优化整个上传系统的关键参考信息,从而得到调试所需的最佳技巧方案。比如我们需要从多家CDN中选出合适的作为接入服务商,那么就可根据比较各家方案的全局均速与成功率判断第三方CDN的上传质量。这里需要强调的是,有些可以明显提升传输速度的优化方案会降低传输成功率,这就使得我们在对比时一定要综合考虑这两项指标;而像分片体积、并发数、重试次数等对上传成功率造成影响的参数,就需要我们通过建立abtest数据闭环纳入考量。
除了上述内容,比较不同平台的上传质量同样至关重要。初期B站仅有Web端上传,使用手机与PC访问Web端上传同样的文件,PC端的成功率明显高于移动端。我们需要在比较不同平台上传质量时尽可能规避由平台限制造成的影响因素,从而得到传输链路本身对用户上传体验的影响。
3. 质量优化
3.1 链路层面优化
链路层面的优化至关重要,其中不可或缺的条件便是CDN。初期我们的方案是接入多家上传CDN,但多家CDN良莠不齐,对用户体验的影响也存在差异;随后我们通过调研发现许多大公司会选择通过建立BGP直连或CDN节点加速的方式传输数据,而这两种方案对边远地区或小规模运营商的支持并不完善;ab对比上传指标同样是链路层面优化的一项重要参考,但最后我们明确了将选用节点数较多的CDN运营商作为数据上传技术支持的思路,CDN公司所提供的节点数量直接决定了数据上传的质量。
3.2 客户端选线VS服务端选线
身处不同地区的用户如何选择最佳传输路线?客户端选线与服务端选线哪一个是最佳方案?这些都是值得我们研究探索的命题。服务端优化的思路之一是我们可通过在服务端建立数据库并记录某一地区用户的上传质量为这一地区分配优选CDN,但在实际应用中,相对于随机分配路线的策略,此方案并未收获令人满意的性能与质量提升;通过进一步探索我们发现,客户端在上传前对线路进行探测能够有助于实现较高传输质量。具体思路是在数据上传前先借助小范围客户端进行一次简单的包探测并与预设方案进行比较从而得到最佳优化方案。
使用电信网络的用户易被上下行宽带不对等的问题所困扰,鉴于此我们与电信运营商进行交涉,通过IP库检测电信用户并为其打开电信上传限制,通过上述优化可使电信用户的上传速率提升10倍左右。
3.3 JSSDK优化
对于JSSDK的优化主要是通过将多文件并发的策略改为文件内分片并发实现,前者相当于多个文件同时占用一个上行总带宽,造成的结果是三个文件的上传均十分缓慢;而后者则可明显提升文件传输效率。
支持将数据上传到第三方存储可以说是我们为用户充分考虑而设计的一项优化,来源于客户端的数据会先被上传至第三方存储并通知原站将其从第三方存储拉取回原站存储,这样的好处在于可以让一些身处距离较远如海外等不方便直连原站存储的用户享受到近距离节点用户的上传体验,例如将原先海外客户端与国内直连改成国外客户端将视频先上传至如亚马逊等第三方海外节点,而后国内原站存储再从第三方海外节点拉取视频数据,用较低成本实现良好传输效果。
3.4 跨会话上传控制
通过大数据我们发现周末上传成功率下降明显,从用户行为的角度分析其原因在于周末Up主人均上传稿件数量增多,很多用户会选择打开两个甚至多个播放器进行并发上传,这对带宽资源的占据是显而易见的。为应对此情况我们使用js的LocalStorage实现锁,实现了将周末视频文件的上传成功率相较于工作日时期提升1%左右的优化。
2018年我们上线了移动端上传服务,其中的一项关键优化在于服务端下发参数以便于数据驱动优化。移动端与Web端最大的区别在于移动端的发版代价更大,通过上报网络环境与主机型号等得到服务端的下发参数,利用数据驱动优化,从而将发版代价巨大的移动端场景转换为与Web浏览器类似的一种易于服务端与数据驱动优化的场景,从而通过调整不同参数实现移动端的最佳上传服务。相对于基于无线网或宽带网络等环境与质量较高的Web浏览器端,基于移动网络的手机移动端可能无法提供传输所需的高质量网络环境,面对这种情况我们会根据网络环境的不同为移动端匹配最合适的节点,优选并发数、分片体积、重试次数与间隔等,确保移动端用户上传体验的一致性。
4. 总结与成果
通过重构系统、建立指标体系、优化指标与明确达成目标等一系列举措,我们得以将上传成功率从最初的的85%提升至94%,同时实现平均速度提升4倍,实现用户相关投诉0记录的优化成果,与此同时我们是业界少有的能够较好支持网页端并行上传的视频平台。
关于未来我们探索的方向,我们希望借助QUIC上传优化移动端的上传体验,并为实现动态切换上传线路而不断努力以达成高可用与更佳的体验优化。
点击【 阅读原文 】或扫描图中二维码了解更多LiveVideoStackCon 2019 上海 音视频技术大会 讲师信息。
原文 https://mp.weixin.qq.com/s/m2ho9X3yMvoG5VR1dzEbTw