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