首发于极光日报
Discord 怎么用 Go 和 C++ 来每天 Resize 1.5 亿张图片

Discord 怎么用 Go 和 C++ 来每天 Resize 1.5 亿张图片

简评:Discord 开源了自家基于 C++ 和 Go 的图片大小调整库 - Lilliput,看起来性能还不错。

Discord 是一款国外的免费实时通话软件,主要面向游戏玩家,因此你可以简单的将其理解为国外的 YY。

尽管 Discord 主要是一个语音和文字的聊天应用程序,但其每天还是会处理超过一亿张图片,而处理这么大量的图片算是一个挺大的技术问题了。如果直接链接图片会把用户的 IP 地址泄露图片主机,并且大图片还会占用大量的带宽。为了避免这些问题,就需要一个中间服务来从用户获取图片,然后调整它们的大小以减少宽带使用。

Image Proxy

在一开始,Discord 创建了一个名为 Image Proxy 的 Python 服务,从远程 URL 获取图像,然后使用 pillow-simd 包来调整图片大小。在此之上还设置了一个缓存层,会缓存部分已经调整了大小的图片。HAProxy 层会将基于 URL 哈希了的请求路由到 Nginx 缓存层,可以很大限度的减少所需调整图片的数量。缓存和代理的组合也足以应对数百万的用户了。

不过,随着用户数量的进一步增长,Image Proxy 也开始出现了不足的地方。最大的问题是没有平均分配工作量,这阻碍了吞吐量。响应时间的差异也很大,有些甚至需要几秒钟才能完成。

正好 Discord 也打算更多的使用 Go,这里也正是个尝试新技术的好地方。

Media Proxy

重写一个正在工作的服务不是一个轻松的决定,不过 Image Proxy 比较简单,因此重写和替换所需的工作也相对比较少。除了更快的响应请求,新服务还可以从 .mp4 和 .webm 视频中获取第一帧。因此新服务叫 Media Proxy。

首先,当然是先对 Go 语言已有的调整图像大小的包进行了测试,但结果并不怎么好。虽然 Go 相对于 Python 普遍情况下会更快,但没有一个已有的包在性能上能优于 Pillow-simd。Go 虽然在处理 HTTP 方面能稍微快一点,但更多的性能瓶颈其实还是在调整图像大小的操作上。

没办法只能动手创建了自己的 Go 语言 Image Resize 库 - Lilliput,通过 Cgo 包装了 OpenCV,并集成了现有的成熟 C 类库(例如,libjpeg-turbo 用于 JPEG,libpng 用于 PNG,giflib 用于 GIFs,libavcodec 用于视频)来处理图像,并通过 fasthttp 处理 HTTP 请求。

顺带还修改了下 giflib 的行为,以便让系统能够逐帧进行解码、调整大小、然后进行编码和压缩,并将第一帧输出为 PNG。并优化了很多问题,比如在 RGB 颜色模式下对 GIF 进行调整。具体实现,可以查看源码。

最后经过测试这个组合的表现大多数情况能优于 pillow-simd。

目前,Discord 已经将 Lilliput 以 MIT 协议开源了出来。如果,你也有高性能调整图片大小的需求的话,不妨试一试这个库。

原文:How Discord Resizes 150 Million Images Every Day with Go and C++

日报扩展阅读:


极光日报,极光开发者旗下媒体。

每天导读三篇英文技术文章。

发布于 2017-11-16 10:02