【问题标题】:Caching reverse proxy for dynamic content缓存动态内容的反向代理
【发布时间】:2018-05-05 12:10:08
【问题描述】:

我本来想在Software Recommendations上提问,但后来我发现这可能是一个太奇怪的请求,需要先澄清一下。

我的观点是:

  • 每个响应都包含一个etag
    • 这是内容的哈希
    • 并且是全球唯一的(具有足够的概率)
  • 内容(大部分)是动态的,并且可能随时更改(expiresmax-age 标头在这里没有用处)。
  • 内容部分取决于用户,由权限赋予(其本身有时会更改)。

基本上,代理应该包含一个将etag 映射到响应内容的缓存。 etag 是从服务器获取的,在最常见的情况下,服务器根本不处理响应内容。

应该如下所示:代理总是向服务器发送请求,然后

  • 1 服务器只返回etag 并且代理根据它进行查找并
    • 1.1 缓存命中,
      • 它从缓存中读取响应数据
      • 并向客户端发送响应
    • 1.2 缓存未命中,
      • 它再次询问服务器,然后
      • 服务器返回带有内容和etag的响应,
      • 代理将其存储在其缓存中
      • 并向客户端发送响应
  • 2 或者服务器返回带有内容和etag的响应,
    • 代理将数据存储在其缓存中
    • 并向客户端发送响应

为简单起见,我省略了if-none-match 标头的处理,这很明显。

我这样做的原因是最常见的情况1.1可以在服务器中非常有效地实现(使用它的缓存映射请求到etags;内容没有缓存在服务器中),因此大多数请求可以在没有服务器处理响应内容的情况下进行处理。这应该比首先从侧面缓存中获取内容然后提供它要好。

在案例 1.2 中,有两个请求到服务器,这听起来很糟糕,但并不比服务器请求一个侧缓存并获得未命中更糟糕。

Q1:我想知道,如何将第一个请求映射到 HTTP。在案例 1 中,它就像一个 HEAD 请求。在案例 2 中,它类似于 GET。两者之间的决定取决于服务器:如果它可以在不计算内容的情况下为etag 提供服务,则为情况 1,否则为情况 2。

Q2:是否有反向代理做这样的事情?我读过关于 nginx、HAProxy 和 Varnish 的文章,但似乎并非如此。这让我想到Q3:这是个坏主意吗?为什么?

Q4:如果不是,那么哪个现有代理最容易适应?

一个例子

来自用户 U1 的 GET 请求(如 /catalog/123/item/456)提供了一些内容 C1etag: 777777。代理将 C1 存储在密钥 777777 下。

现在同样的请求来自用户U2。代理转发它,服务器只返回etag: 777777,代理很幸运,在其缓存中找到C1(案例1.1)并将其发送到U2在这个例子中,客户端和代理都不知道预期的结果。

有趣的部分是服务器如何在不计算答案的情况下知道etag。例如,它可以有一条规则,说明这种形式的请求为所有用户返回相同的结果,假设允许给定用户查看它。因此,当来自U1 的请求到来时,它计算C1 并将etag 存储在密钥/catalog/123/item/456 下。当同样的请求来自U2 时,它只是验证了U2 被允许查看结果。

【问题讨论】:

  • 您描述的是HTTP中的条件GET。客户端使用一些特定的 HTTP 标头执行 GET,告诉服务器仅在特定条件匹配或不匹配时才回复内容,例如基于有效日期或 ETag 的条件。
  • @PatrickMevzek 然后我的描述令人困惑。我知道有条件的 GET,这是不同的。它假定发起者“猜测”响应的可能etag(它甚至可能在if-none-match 标头中发送more than one)。 +++ 在这里,代理无需猜测即可查询服务器,服务器通常(案例 1)仅以 etag 响应,希望代理获得缓存命中(案例 1.1)。还有可能进行第二次查询(案例 1.2)。
  • 不,它不假定客户端猜到任何东西,因为 ETag 值在设计上是不透明的。客户端发送它在缓存中的 ETag 值,与它查询的 URL 相关。
  • @PatrickMevzek “猜测”是指发送最近收到的etag。从某种意义上说,它是“猜测”,它可能是对的,也可能是错的。 +++ 在我上面的例子中,两个客户端都没有发送etag,因为这是他们的第一次访问。代理可能已经看到很多etags,但它不知道哪个可以申请给定的请求,因为响应是特定于用户的并且它缺乏相应的逻辑。

标签: http caching reverse-proxy


【解决方案1】:

Q1:是一个GET请求。服务器可以不带正文回答“304 未修改”。

Q2openresty(带有一些附加模块的 nginx)可以做到,但您需要自己实现一些逻辑(见下文更详细的描述)。

Q3:考虑到您问题中的信息,这听起来是一个合理的想法。只是一些思考的食物:

  • 您还可以将页面拆分为用户特定部分和通用部分,这些部分可以独立缓存。

  • 您不应期望缓存永远保留计算出的响应。因此,如果服务器返回 304 not modifiedetag: 777777(根据您的示例),但缓存不知道它,您应该可以选择强制重新构建答案,例如另一个带有自定义标头 X-Force-Recalculate: true 的请求。

  • 不完全是您问题的一部分,但是:确保设置正确的 Vary 标头以防止出现缓存问题。

  • 如果这只是关于权限,您也许还可以使用签名 cookie 中的权限信息。缓存可以在不询问服务器的情况下从cookie中获取权限,并且cookie由于签名是防篡改的。

Q4:我会为此使用 openresty,特别是 lua-resty-redis module。将缓存的内容放入以etag为键的redis键值存储。您需要在 Lua 中编写查找逻辑代码,但不应超过几行代码。

【讨论】:

  • 非常感谢!一些细节:我认为服务器不允许在没有正文的情况下响应,因为请求不是有条件的,因为代理不发送 if-none-match 标头。一般来说,它缓存的任何etag 都不符合包含在标头中的条件。 +++关于X-Force-Recalculate: true,这是我的情况1.2(我不清楚)。 +++ 签名 cookie 中的权限如果不能被撤销并且没有那么多权限,那将是一个绝妙的主意。有些内容仅涉及权限,而其他内容则更复杂。 +++ 我会研究一下 openresty。
  • 关于 304,你说得对,if-none-match 根据RFC7232 4.1 是强制性的,但我想这是按照 HTTP 语义精神对您的需求建模的最佳选择。跨度>
猜你喜欢
  • 2016-04-04
  • 1970-01-01
  • 1970-01-01
  • 2016-08-17
  • 2019-01-12
  • 2015-01-04
  • 2019-06-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多