Help us learn about your current experience with the documentation. Take the survey.

图片缩放指南

本节简要介绍了 GitLab 图片缩放器及其使用方法。

如果您想了解 GitLab 图片缩放的历史背景,可能会对 这篇 Unfiltered 博客文章感兴趣。

为什么要进行图片缩放?

自版本 13.6 起,GitLab 按需缩小图片以减少页面数据占用。 这既减少了"网络传输"的数据量,也有助于提高渲染性能, 因为浏览器需要处理的工作量减少了。

何时进行图片缩放?

通常情况下,当客户端通过在查询字符串中添加 width 参数来请求图片资源时,会触发图片缩放器。但是,我们只对特定类型和格式的图片进行缩放。 是否允许对图片进行重新缩放,由硬编码规则和配置设置共同决定。

硬编码规则仅允许:

此外,Workhorse 中的配置可能导致图片缩放器拒绝请求,如果:

  • 图片文件过大(由 max_filesize 控制,我们只重新缩放不超过配置字节大小的图片,参见 max_filesize)。
  • 已有太多图片缩放器正在运行(由 max_scaler_procs 控制)。

例如,以下两个 URL 都提供 GitLab 项目头像,一个为原始尺寸,另一个缩小到 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 头字段进行匹配。只有当原始图片已更改且需要重新缩放时,我们才会再次运行缩放器。