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

Vary based on cookie without producing duplicate cache entries

我正在开发一个 multi-tenant 平台,我们的客户可以在该平台上创建在线商店。商店可能同时销售多种货币的产品,例如欧元和美元。

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

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

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);
}

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

我的问题源于这样一个事实,即我事先无法知道商店的默认货币(在我们的例子中是欧元或美元),因为店主可以 add/remove 他们动态地。

因此,在对 /homepage 端点的第一个请求中,用户不会有 currency cookie,因此它的值将为 '',这将在 hash_data方法。

服务器识别出客户端没有货币cookie,所以它设置了一个Set-Cookie header,当页面命中浏览器时,客户端就会有cookie。

目前,我们有 /homepage 的缓存条目,没有 currency cookie。

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

这一次,有一个currency cookie,在hash_data函数中使用,但它产生了不同的密钥,所以它再次命中后端,但输出将是相同(显然,Varnish 并不知道)。

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

有没有我可以采用的替代方法来解决这个问题,或者某种修复方法?

我最初的思路是:

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

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

为简单起见,我没有提供 100% 的 vcl 配置,但我使用默认 vcl 作为模板为 vcl_recvvcl_backend_response 创建自定义配置。我知道 cookie 意味着个性化内容,我不应该缓存它,但这种情况下的个性化对这个用户来说并不是唯一的,所以我做了一个例外。

编辑

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

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

目前缓存中没有任何内容

  1. 用户第一次访问 homepage,他没有任何 cookies
  2. Varnish 根据http.x-currency 生成一个散列,该散列为空。结果没中。
  3. varnish打到后端,后端看到缺少x-currencyheader,所以设置了一个Set-Cookieheader,设置cookie为USD
  4. Varnish 接收响应,缓存它,并将它发送回客户端
  5. 同一用户在同一页面上点击刷新
  6. Varnish 根据 http.x-currency 生成哈希,现在是 USD(上次是空的),导致缓存未命中,再次
  7. Varnish 去后端,后端returns 完全相同的响应,因为没有x-currencyx-currency = USD 对后端来说是相同的
  8. Varnish 接收响应,缓存它并returns它到客户端

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

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

在我看来,好像我只能忍受这个。

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

URL匹配

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

这是一个例子:

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

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

发送一个变化header

您还可以通过发送以下 Vary 响应 header 来管理应用程序中的条件变化方面:

Vary: x-currency

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

请记住,x-currency 请求 header 需要发送到应用程序。这不是真正的问题,因为您已经在 vcl_recv.

中设置了这个 header

It is important to strip off this part of the Vary header in vcl_deliver because the client has no awareness of this request header.

主页问题的重复键

在你上面的(更新的)问题以及下面的评论中,你谈到了假设的 /homepage object 基于 x-currency header.

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

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

如果 /homepage 路由产生完全相同的 about 而不管 x-currency 值,这意味着 x-currency 不应该是散列的一部分。这也意味着 URLhost header 将构成散列的基础。

通过不在 hash_data() 中添加 x-currency 或作为 Vary header 的一部分,/homepage 将只有一个版本。

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

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

另一个重要的评论是关于 return Set-Cookie header 如果未设置货币的页面。

Varnish 的标准行为将导致 so-called Hit-For-Miss.

这意味着 object 没有存储在缓存中,因为您不想为每个人缓存 Set-Cookie header。

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

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

在命中率方面的结果是,由于 Set-Cookie.

,每个用户的第一个请求都将是缓存未命中

请记住这一点。

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

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