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

使用 ETag 缓存进行轮询

轮询变更(反复向服务器询问是否有新变更) 会给 GitLab 实例带来高负载,因为它通常需要执行至少几次 SQL 查询。这使得扩展大型 GitLab 实例(例如 GitLab.com)变得非常困难,因此我们不允许添加需要轮询并访问数据库的新功能。

你应该使用带有 ETag 缓存的轮询机制,并将缓存存储在 Redis 中。

如何使用

  1. 将你想要轮询的端点路径添加到 Gitlab::EtagCaching::Router 中。
  2. 使用 Gitlab::PollingInterval.set_header 为响应设置轮询间隔头。
  3. 使用 Gitlab::EtagCaching::Store 为你的端点路径实现缓存失效。每当资源发生变化时,你必须使依赖于该资源的路径的 ETag 失效。
  4. 检查机制是否正常工作:
    • 请求应返回状态码 304
    • log/development.log 中不应记录任何 SQL 查询

ETag 缓存轮询的工作原理

缓存未命中(Cache Miss):

%%{init: { "fontFamily": "GitLab Sans" }}%%
sequenceDiagram
   Client->>+Rails: GET /projects/5/pipelines
   Rails->>+EtagCaching: GET /projects/5/pipelines
   EtagCaching->>+Redis: read(key = 'GET <ETag>')
   rect rgb(255, 204, 203)
     Redis->>+EtagCaching: cache MISS
   end
   EtagCaching->>+Redis: write('<New ETag>')
   EtagCaching->>+Rails: GET /projects/5/pipelines
   Rails->>+Client: JSON response with ETag

缓存命中(Cache Hit):

%%{init: { "fontFamily": "GitLab Sans" }}%%
sequenceDiagram
   Client->>+Rails: GET /projects/5/pipelines
   Rails->>+EtagCaching: GET /projects/5/pipelines
   EtagCaching->>+Redis: read(key = 'GET <ETag>')
   rect rgb(144, 238, 144)
     Redis->>+EtagCaching: cache HIT
   end
   EtagCaching->>+Client: 304 Not Modified
  1. 每当资源发生变化时,我们会生成一个随机值并将其存储在 Redis 中。
  2. 当客户端发起请求时,我们将 ETag 响应头设置为 Redis 中的值。
  3. 客户端缓存响应(客户端缓存),并在每次对同一资源的后续请求中,将 ETag 作为 If-None-Match 头发送。
  4. 如果 If-None-Match 头与 Redis 中的当前值匹配,我们就知道资源没有发生变化,因此可以立即发送 304 响应,而无需查询数据库。客户端的浏览器会使用缓存的响应。
  5. 如果 If-None-Match 头与 Redis 中的当前值不匹配,我们就必须生成新的响应,因为资源已经发生了变化。

对于希望启用 ETag 缓存的端点,不要使用查询参数(例如 ?scope=all)。中间件只考虑请求路径,并忽略查询参数。所有参数都应包含在请求路径中。通过这样做,我们可以避免查询参数的排序问题,并使路由匹配更加简单。

更多信息请参阅: