图像缩放指南
这部分内容简要介绍了极狐GitLab 图像缩放器及其工作原理。
关于极狐GitLab 图像缩放历史的总体介绍,你可能会对 这篇 Unfiltered 博客文章感兴趣。
为什么需要图像缩放?
从 13.6 版本开始,极狐GitLab 会按需缩小图像以减少页面数据占用。 这不仅减少了“网络传输时”的数据量,而且由于浏览器需要做的工作更少,也有助于提升渲染性能。
我们何时缩放图像?
通常,当客户端通过在查询字符串中添加 width 参数来请求图像资源时,会触发图像缩放器。但是,我们只会缩放特定种类和格式的图像。 我们是否允许对图像进行缩放,由硬编码规则和配置设置共同决定。
硬编码规则只允许缩放:
此外,Workhorse 中的配置可能会导致图像缩放器在以下情况下拒绝请求:
- 图像文件过大(由 max_filesize 控制,我们只缩放不超过配置字节大小的图像,参见 max_filesize)。
- 正在运行的图像缩放器过多(由 max_scaler_procs 控制)。
例如,以下两个不同的 URL 分别提供原始大小和缩小到 64 像素的极狐GitLab 项目头像。只有第二个请求会触发图像缩放器:
- https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/source/images/gitlab-logo-extra-whitespace.png
- https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/source/images/gitlab-logo-extra-whitespace.png?width=64
我们在哪里缩放图像?
目前,Rails 和 Workhorse 协作进行图像缩放。这是极狐GitLab 中常见的实现和性能模式:重要的业务逻辑(如请求身份验证和校验)在 Rails 中进行,而“繁重的工作”(缩放和提供二进制数据)则在 Workhorse 中发生。
整体请求流程如下:
Rendering chart...
Rails
目前,图像缩放仅限于 Upload 实体,特别是上面提到的头像。 因此,Rails 中所有与图像缩放相关的逻辑目前都位于 send_file_upload 控制器混入模块中。在收到通过 Workhorse 从客户端发送的请求后,我们会根据上述标准检查是否应该触发图像缩放器,如果是,则渲染一个特殊的响应头字段 (Gitlab-Workhorse-Send-Data),其中包含 Workhorse 执行缩放请求所需的参数。如果 Rails 判定该请求不构成有效的图像缩放请求,我们会遵循提供任何普通上传的路径。
Workhorse
假设 Rails 判定请求有效,Workhorse 将接管处理。在通过 Rails 响应收到 send-scaled-image 指令后,一个特殊的响应注入器将被调用,它知道如何缩放图像。它需要的唯一输入是图像的位置(如果图像位于块存储中,则为路径;否则为指向远程存储的 URL)和所需的宽度。 Workhorse 将透明地处理位置,因此 Rails 无需关心图像实际存放的位置。
此外,为了在 Rails 中进行请求校验,Workhorse 将运行几个前置条件检查,以确保我们确实可以缩放图像,例如确保我们不会超出缩放器进程预算,以及文件是否符合配置的最大允许尺寸约束(以控制内存消耗)。
为了实际缩放图像,Workhorse 最终会 fork 出一个子进程来执行实际的缩放工作,并将结果流式传输回客户端。
缓存缩放后的图像
我们目前不将缩放后的图像存储在任何地方;每次请求较小版本时,缩放器都会运行。 但是,Workhorse 实现了标准的条件 HTTP 请求策略,如果客户端缓存中的图像是最新的,则允许我们跳过缩放器。 为此,我们传输一个携带原始图像文件的 UTC 时间戳的 Last-Modified 头字段,并将其与客户端请求中的 If-Modified-Since 头字段进行匹配。 只有当原始图像已更改且需要重新缩放时,我们才会再次运行缩放器。