本文翻译自 How Twitter Handles 3,000 Images Per Second ,作者是 Todd Hoff ,翻译已获得本人授权。
今天的Twitter每秒钟能够创建并保存3000张(200GB)图片。甚至早在2015年,Twitter就通过改进媒体存储策略节约了6百万美元。
以前并不是这样的。Twitter在2012年主要以提供文字信息为主,当时的用户也很少发布各种炫酷的动态图片。到现在2016年,Twitter开始提供富媒体功能。这样的转变是通过Twitter自行开发的全新 媒体平台(Media Platform) 实现的,这个平台可以支持照片预览、多张照片发送、动态Gif图片、Vine视频,以及内嵌视频等功能。
Twitter软件开发工程师 Henna Kermani 在 Mobile @Scale London 活动中,通过一场名为 每秒3000张图片 的有趣演讲介绍了Twitter的媒体平台。演讲主要侧重于图像处理方面,但她讲的大部分细节也同样适用于其他形式的媒体。
演讲中一些比较有趣的重点包括:
Twitter开发富媒体功能的过程中还发生了一些很棒的故事,一起来学学他们是如何做到的...
用户在某个应用中撰写了一条推文,并可能在推文中附加了一张图片。
问题1:浪费大量网络带宽
上传过程充满不确定性,或者完全成功或者彻底失败。失败可能出于任何原因,例如网络卡顿或传输错误等,如果失败就只能从头开始重新执行整个上传过程,包括上传图片。例如,假设上传操作在进行到95%后出错,也需要将所有内容重新上传一遍。
问题2:面对新出现更大体积的媒体无法很好地缩放
问题3:内部带宽使用效率低下
端点需要连接至负责处理用户身份验证和路由的Twitter前端(TFE),随后用户会被路由至某个图片服务(Image Service)。
图片服务联系变体生成器(Variant Generator),用不同尺寸(例如小、中、大、缩略图)为图片生成不同实例。这些变体会存储在BlobStore中,这是一种专为图片和视频等大型载荷进行过优化的键值(key-value)类型存储。随后图片将永远存储在这里。
在创建和保存推文的过程中,还涉及到很多其他服务。因为端点是单体的,如果将媒体与推文元数据结合在一起,就需要在所有服务之间传输这种绑定在一起的数据。这样的大型载荷甚至会传递到原本在设计上并不用于直接处理图片的服务,这些服务甚至并不是媒体传递渠道的一部分,但依然需要针对大型载荷的处理进行优化。这样的做法导致内部带宽使用效率严重降低。
问题4:存储容量极度膨胀
用户看到一条包含图片的推文。图片来自哪里?
客户端从CDN请求图片的变体。CDN可能需要从原始位置或TFE处检索该图片。这一过程最终导致需要通直接查询BlobStore的方式,使用URL请求某一特定尺寸的图片。
问题5:无法引入新的变体
这种设计不是非常灵活。如果需要添加新的变体,也就是说需要为图片创建一个新的尺寸,此时必须为BlobStore中的每个图片创建一个新尺寸的版本。这种方式缺乏按需创建变体的便利机制。
缺乏灵活性意味着Twitter很难为客户端添加新的功能。
将上传的媒体与推文去耦合。
上传操作至此成为“一等公民”,并创建了专门用于将原始图片存储至BlobStore的上传端点。
这种方法为上传操作的处理提供了极大的灵活性。
客户端联系TFE,TFE随后联系图片服务,图片服务将图片保存至BlobStore并将相关数据添加至一个元数据存储。仅此而已。这一过程不会涉及任何隐藏的服务,不需要处理媒体,也不需要四处传递图片。
随后图片服务会返回一个代表该媒体的唯一标识符,即mediaId。当客户端需要创建推文,发布私信,或更新自己的头像照片时,将使用这个mediaId作为引用该媒体的句柄,而不需要提供媒体的原始文件。
假设用户想要使用刚上传的图片发布推文,过程将会是这样:
客户端联系更新端点,在推文中包含mediaId,该请求将发送至Twitter前端,随后TFE会将请求路由至对于所创建内容来说最为恰当的服务。对推文本身,最适宜的服务是TweetyPie,私信和用户资料信息的处理也由不同服务进行,所有这些服务都能与图片服务通信,图片服务器中包含处理面孔检测、儿童色情内容检测等功能所需的推文处理队列,当这些任务执行完毕后,图片服务会与处理图片的ImageBird或处理视频的VideoBird服务通信。ImageBird负责生成变体,VideoBird则对视频进行一定的转码,最终处理生成的媒体内容将保存至BlobStore。
不再需要将媒体内容四处传输,借此可节约大量本被浪费的带宽。
分块可续传的上传。
进入地铁站,10分钟后出来,上传过程可从上次中断的地方恢复进行。对用户来说该过程是完全无缝的。
客户端使用上传API发起上传会话,后端会为用户提供一个mediaId,这个mediaId将在整个上传会话中充当标识符。
图片被拆分为多个小块,例如拆分成三块。这些文件块可使用API附加到一起,每次调用的附加操作可提供必要的片段索引,所有附加操作都可作用于同一个mediaId。上传完成后对上传内容进行“定稿”,随后这个媒体就可以使用了。
这种方法更容易适应网络故障。每个独立小块可以重试,如果网络因为任何原因中断,用户可以暂停并在网络恢复后从暂停的位置继续上传。
方法简单,效益巨大。对于体积超过50KB的文件,上文提到的三个国家中图片上传失败率降低幅度分别为:巴西,33%;印度,30%;印度尼西亚,19%。
此处用到了一种名为MinaBird的CDN源服务器。
MinaBird可与ImageBird和VideoBird通信,这样就算不存在,也可以即时生成不同尺寸图片和不同格式视频的变体。
在处理客户端请求方面,MinaBird更流畅也更动态。举例来说,假设有内容由于DMCA(数字千年版权法)的要求需要删除,此时很容易便可阻止对相关内容的访问,或重新允许对媒体特定片段的访问。
按需即时生成变体和转码的方式使得Twitter在存储容量的使用方面更为高效。
按需生成变体,意味着不需要将所有变体都存储在BlobStore中,这是一个巨大的进步。
原始图片在删除前将一直保留,而变体只保留20天。媒体平台团队针对最佳过期时限进行了大量研究,发现在所有请求的图片中,有大约50%的图片都是在最多15天(左右)的时间内上传的。继续保留更早前上传的图片可以获得的收益在逐渐下降。更老的媒体文件也有可能就此无人问津。15天后存在一条很长的长尾。
不设置存活时间(TTL)并且不过期的情况下,媒体文件的存储导致数据存储总量每天增加6TB,按需生成所有变体的“偷懒”做法会让数据存储总量每天增加1.5TB。20天存活时间所用的存储空间并不像“偷懒”做法那么多,因此在存储方面的成本并不高,但对计算的要求更高了。对于“偷懒”的做法,在读取的同时生成所有变体,需要为每个数据中心提供150台ImageBird服务器,而20天存活时间的做法只需要投入75台。因此20天存活时间是一个甜区,可以在存储和计算方面实现平衡。
由于节约存储和计算资源等同于省钱,通过采取20天存活时间的做法,Twitter在2015年节约了6百万美元。
针对Google创建的图像格式 WebP 执行了为期6个月的实验。
相比PNG或JPEG图片,这种格式的图片体积平均减小25%。
用户参与积极性有所提高,尤其是在减小图片体积可以帮助网络减压的新兴市场。
iOS 不支持该格式。
仅 Android 4.0 以上系统可支持。
平台支持的缺乏使得WebP的支持代价不菲。
Twitter还尝试过渐显式JPEG。这种格式可以使用逐行扫描的方式进行渲染,首次扫描的图片可能显得斑驳不匀,但可通过逐行扫描的方式逐渐进行完善。
性能更好。
后端易于支持。
相比传统JPEG编码速度慢60%。但由于编码工作只需要进行一次,随后所有用户都可从中受益,因此这不算什么大问题。
不支持透明,因此还需保留透明PNG,但渐显式JPEG其他方面都很出色。
客户端对该格式的支持是通过 Facebook的Fresco 库实现的。Fresco库的价值很值得大书特书,就算在2G网络中也能实现让人印象深刻的效果。PJPEG的首次扫描只产生10kb流量,因此很快就可以加载完成。当原生渠道还在等待加载,无法显示任何内容的时候,PJPEG已经可以提供可分辨的图片。
通过对推文详细信息视图的加载进行持续的实验发现,p50加载时间降低9%,p95加载时间降低27%,出错率降低了74%。网络速度缓慢的用户无疑能从中获得不菲的收益。
感谢郭蕾对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ,@丁晓昀),微信(微信号: InfoQChina )关注我们。