缓存动态内容的反向代理

maa*_*nus 10 caching reverse-proxy http

我正在考虑询问软件建议,但后来我发现它可能是一个太奇怪的请求,它需要先澄清一些.

我的观点是:

  • 每个回复包含一个 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:这是个坏主意吗?为什么?

问题4:如果没有,那么哪个现有代理最容易适应?

一个例子

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

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

有趣的是,服务器如何在etag不计算答案的情况下知道.例如,它可以有一条规则,声明此表单的请求为所有用户返回相同的结果,假设允许给定用户查看它.所以当来自请求时U1,它会计算C1并存储etag在密钥下面/catalog/123/item/456.当相同的请求来自时U2,它只是验证了U2允许查看结果.

小智 2

Q1:这是一个 GET 请求。服务器可以用没有正文的“304 not modded”来应答。

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

Q3:鉴于您问题中的信息,这听起来是一个合理的想法。只是一些值得深思的东西:

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

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

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

  • 如果这仅与权限有关,您也许还可以使用签名 cookie 中的权限信息。缓存无需询问服务器即可从 cookie 中获取权限,并且 cookie 由于签名而具有防篡改功能。

Q4:我会使用 openresty,特别是lua-resty-redis 模块。将缓存的内容放入带有 as 键的 Redis 键值存储中etag。您需要在 Lua 中编写查找逻辑,但不应超过几行。