强制请求未命中缓存但仍存储响应

强制请求未命中缓存但仍存储响应

我有一个运行缓慢的 Web 应用,我在它前面放置了 Varnish。所有页面都是静态的(不会因用户不同而变化),但它们需要每 5 分钟更新一次,以便包含最新数据。

我有一个简单的脚本(wget --mirror),每 15 分钟抓取一次整个网站。每次抓取大约需要 5 分钟。抓取的目的是更新 Varnish 缓存中的每个页面,这样用户就不必等待页面生成(因为所有页面都是最近由蜘蛛生成的)。

时间线如下:

  • 00:00:00:缓存已刷新
  • 00:00:00:蜘蛛开始爬行,用新页面更新缓存
  • 00:05:00:蜘蛛结束爬行,所有页面更新至00:15:00

在 0:00:00 到 0:05:00 之间发出的请求可能会访问尚未更新的页面,并且将被迫等待几秒钟才能得到响应。这是不可接受的。

我想做的是,也许使用一些 VCL 魔法,始终将请求从蜘蛛转发到后端,但仍将响应存储在缓存中。这样,用户将绝不必须等待页面生成,因为没有 5 分钟的窗口期使缓存的某些部分为空(服务器启动时除外)。

我怎样才能做到这一点?

答案1

req.hash_always_miss应该可以解决问题。

不要在蜘蛛运行开始时执行完全缓存刷新。相反,只需将蜘蛛设置为工作 - 并在您的 中将vcl_recv蜘蛛的请求设置为始终错过缓存查找;它们将从后端获取新副本。

acl spider {
  "127.0.0.1";
  /* or whereever the spider comes from */
}

sub vcl_recv {
  if (client.ip ~ spider) {
    set req.hash_always_miss = true;
  }
  /* ... and continue as normal with the rest of the config */
}

在此过程中,直到新的响应进入缓存中,客户端将继续无缝地获取旧的缓存(只要它仍在其 TTL 范围内)。

答案2

Shane 的上述回答比这个好。这是一个替代解决方案,但更复杂,而且存在其他问题。请为 Shane 的回答点赞,而不是这个。我只是展示了另一种解决问题的方法。


我最初的想法是return (pass);vcl_recv,然后在请求被获取之后,以vcl_fetch某种方式指示 Varnish应该缓存该对象,即使它之前已经被明确传递过。

事实证明这不可能

如果您选择在较早的 VCL 函数(例如:vcl_recv)中传递请求,您仍将执行 vcl_fetch 的逻辑,但是即使您提供了缓存时间,对象也不会进入缓存。

因此,次优方案是像普通请求一样触发查找,但要确保它总是失败。没有办法影响查找过程,所以它总是会命中(假设它缓存;如果没有,那么它无论如何都会丢失并存储)。但我们可以影响vcl_hit

sub vcl_hit {
    # is this our spider?
    if (req.http.user-agent ~ "Wget" && client.ip ~ spider) {
        # it's the spider, so purge the existing object
        set obj.ttl = 0s;
        return (restart);
    }

    return (deliver);
}

我们无法强制它不使用缓存,但我们可以从缓存中清除该对象并重新启动整个过程。现在它回到开头,在vcl_recv,它最终会再次进行查找。由于我们已经清除了我们尝试更新的对象,因此它将丢失,然后获取数据并更新缓存。

有点复杂,但确实有效。用户在清除和存储响应之间卡住的唯一时间是单个请求处理的时间。虽然不完美,但还不错。

相关内容