根据 cookie 变化而不会产生重复的缓存条目

问题描述

我正在开发一个多租户平台,我们的客户可以在其中创建在线商店。一家商店可能会同时以多种货币(例如欧元和美元)销售产品。

由于显示的产品价格因货币而异,我希望为欧元和美元缓存页面

让它工作的最初部分相当容易,但我遇到了不希望的副作用。

sub vcl_recv {
    if (req.http.cookie) {
        cookie.parse(req.http.cookie);
        // If the user has a `currency` cookie,set it as a header
        set req.http.x-currency = cookie.get("currency");
    }
    ...
}

sub vcl_hash {
    hash_data(req.http.x-currency);
}

我的后端识别使用此标头或不使用此标头以呈现正确的输出,然后 Varnish 存储结果。如果请求没有这个标头,服务器会发送一个 Set-Cookie 标头,它会告诉浏览器使用商店的认货币创建一个 cookie。

我的问题源于这样一个事实,即我无法事先知道商店的认货币(在我们的例子中是欧元或美元),因为所有者可以动态添加/删除它们。

>

因此,在对 /homepage 端点的第一个请求中,用户将没有 currency cookie,因此其值为 '',这将在 {{1} } 方法

服务器识别出客户端没有货币 cookie,因此设置了一个 hash_data 标头,当页面点击浏览器时,客户端就会拥有 cookie。

目前,我们有一个 Set-Cookie 的缓存条目,没有 /homepage cookie。

客户点击刷新,请求到达 Varnish。

这次有一个currency cookie,在currency函数中使用,但是产生的key不同,所以再次命中后端,但是输出会完全一样(显然,Varnish 不知道这一点)。

hash_data url 有两个关联的缓存条目,它们具有相同的内容

是否有其他方法可以解决这个问题,或者某种修复方法

我最初的思路是:

进入 /homepage 子例程并检查请求。如果请求缺少 cookie,则 vcl_backend_response 对象将在其 beresp 中包含商店的认货币。我可以使用它来创建 2 个不同的缓存键,它们指向同一个缓存条目 - Set-Header 用于空 cookie 和 /homepage + ''

这在技术上是不可能的,但它说明了似乎正在解决我的问题。

为简单起见,我没有提供 100% 的 vcl 配置,但我使用认 vcl 作为模板为 /homepage + 'EUR'vcl_recv 创建自定义配置。我知道 cookie 意味着个性化内容,我不应该缓存它,但在这种情况下,个性化并不适合该用户,所以我破例了。

编辑

www.mystore.com/en/ - 认货币为美元

vcl_backend_response

我们目前缓存中没有任何内容

  1. 用户第一次访问 sub vcl_hash { if(req.url !~ "^/(contact|sitemap)") { hash_data(req.http.x-currency); } } ,他没有任何 cookie
  2. Varnish 基于 homepage 生成一个散列,它是空的。结果是未命中。
  3. Varnish 命中后端,后端发现缺少 http.x-currency 标头,因此设置了 Set-Cookie 标头,将 cookie 设置为 USD
  4. Varnish 接收响应,将其缓存,然后将其发送回客户端
  5. 同一用户在同一页面上刷新
  6. Varnish 根据 x-currency 生成一个哈希值,现在是 USD(上次为空),这再次导致缓存未命中
  7. Varnish 进入后端,后端返回完全相同的响应,因为没有 http.x-currencyx-currency = USD 意味着后端相同
  8. Varnish 接收响应,缓存它并返回给客户端

主页路由现在已完全缓存。无论某人使用 x-currency cookie 访问还是没有访问,他都会收到缓存的响应。但是,缓存寄存器中有两个条目,一个是空的 currency=USD cookie,一个currency cookie,两者的结果相同。

问题源于这样一个事实:当请求到达并且它没有 currency=USD cookie 时,如果没有后端的帮助,我没有一种可预测的方法来计算它的值。

在我看来,我似乎只能忍受这个。

解决方法

据我所知,您只想为包含不同货币的页面创建缓存变体。根据您提供的信息,我假设主页在内容方面没有区别,也不需要变化。

网址匹配

您可以匹配某些不需要变体的网址格式并有条件地执行变体。

这是一个例子:

sub vcl_hash {
    if(req.url !~ "^/(homepage|contact|sitemap)") {
        hash_data(req.http.x-currency);
    }
}

这是一个过于简化的示例,但我希望您理解可以有条件地进行变体。使用包含或排除模式。

发送 Vary 标头

您还可以通过为需要按货币变化的页面发送以下 Vary 响应标头来管理应用程序中的条件变化方面:

Vary: x-currency

通过这样做,您可以从应用程序内部管理变体,而不是依赖于 hash_data() 中的 sub vcl_hash

请记住,x-currency 请求标头需要发送到应用程序。这不是真正的问题,因为您已在 vcl_recv 中设置了此标头。

去掉 Varyvcl_deliver 标头的这一部分很重要,因为客户端不知道这个请求标头。

首页问题的重复键

在您上面的(更新的)问题以及下面的评论中,您谈到了基于 /homepage 标头多次缓存的假设 x-currency 对象。

这对你来说应该不是问题。我注意到您了解 Varnish 中的散列是如何工作的。

我上面提到的 2 个解决方案(URL 匹配和变化)解释了如何有条件地扩展哈希。术语有条件在这里非常重要。

如果 /homepage 路由无论 x-currency 值如何都产生完全相同的结果,则意味着 x-currency 不应成为散列的一部分。这也意味着 URL主机头 将构成散列的基础。

如果不在 x-currency 中或作为 hash_data() 标头的一部分添加 Vary/homepage 将只有一个版本。

您表示 Vary: x-currency 是您的首选解决方案。我建议您只为每种货币具有不同输出的页面设置它。

我的印象是您的问题可以通过有条件地改变货币来解决。

另一个重要的说明是关于在未设置货币的情况下返回 Set-Cookie 标头的页面。

Varnish 的标准行为将导致所谓的Hit-For-Miss

这意味着该对象未存储在缓存中,因为您不想为每个人缓存 Set-Cookie 标头。

在接下来的 2 分钟内,对该资源的所有请求都将绕过缓存,除非下一个响应变为可缓存。

在您的情况下,对 /homepage 的第二个请求将变为可缓存的,因为响应将不再包含 Set-Cookie

就命中率而言,结果是每个用户的第一个请求都将是缓存未命中,因为 Set-Cookie

请记住这一点。

就我个人而言,我不会担心这一点,但如果您这样做,您实际上可以去除 beresp.http.Set-Cookie 以使页面可缓存并自己在 vcl_deliver 中设置 cookie。

当然,您需要访问决定 EURUSD 的逻辑,以正确设置 cookie 值。