缓存动态内容的反向代理

时间:2017-11-21 21:32:24

标签: http caching reverse-proxy

我正在考虑问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:这是个坏主意吗?为什么呢?

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

示例

来自用户/catalog/123/item/456的{​​{1}}等GET请求已提供了一些内容U1C1。代理将etag: 777777存储在密钥C1下。

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

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

1 个答案:

答案 0 :(得分:2)

Q1 :这是一个GET请求。服务器可以在没有正文的情况下回答“304未修改”。

Q2 openresty(带有一些额外模块的nginx)可以做到,但您需要自己实现一些逻辑(请参阅下面的详细说明)。

Q3 :根据您提问中的信息,这听起来是个合理的想法。只是一些值得思考的东西:

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

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

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

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

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