我对HTTP缓存感到困惑

时间:2009-01-11 16:51:14

标签: http rest caching

我一直在考虑在RESTful环境中批量读取和写入,我想我已经意识到我对HTTP缓存有更广泛的问题。 (下面我使用逗号(“,”)来分隔多个记录ID,但该细节并不特定于讨论。)

我从这个问题开始:

1。单个GET由批量更新无效

GET /farms/123         # get info about Old MacDonald's Farm
PUT /farms/123,234,345 # update info on Old MacDonald's Farm and some others
GET /farms/123

客户端和Farms服务器之间的缓存服务器如何知道在/farms/123看到PUT的缓存时无效?

然后我意识到这也是一个问题:

2。批量GET由单个(或批量)更新无效

GET /farms/123,234,345 # get info about a few farms
PUT /farms/123         # update Old MacDonald's Farm
GET /farms/123,234,345

当缓存看到PUT过去时,缓存如何知道多场GET无效?

所以我认为问题实际上只是批处理操作。然后我意识到任何关系都可能导致类似的问题。假设一个农场拥有零个或一个所有者,而一个所有者可以拥有零个或一个农场。

3。单个GET因更新相关记录而失效

GET /farms/123   # get info about Old MacDonald's Farm
PUT /farmers/987 # Old MacDonald sells his farm and buys another one
GET /farms/123

当看到PUT过去时,缓存如何知道使单个GET无效?

即使您使用关系模型将模型更改为RESTful,也会遇到同样的问题:

GET    /farms/123           # get info about Old MacDonald's Farm
DELETE /farm_ownerships/456 # Old MacDonald sells his farm...
POST   /farm_ownerships     # and buys another one
GET    /farms/123

在#3的两个版本中,第一个GET应该返回类似(在JSON中)的内容:

farm: {
  id: 123,
  name: "Shady Acres",
  size: "60 acres",
  farmer_id: 987
}

第二个GET应该返回类似的内容:

farm: {
  id: 123,
  name: "Shady Acres",
  size: "60 acres",
  farmer_id: null
}

但它不能!即使你恰当地使用ETag也不行。您不能指望缓存服务器检查ETag的内容 - 内容可以加密。并且您不能指望服务器通知缓存记录应该失效 - 缓存不会自己注册服务器。

那么我缺少标题吗?在某些资源的任何HEAD之前,指示缓存的事情应该GET?如果我可以告诉缓存哪些资源可能经常更新,我想我可以接受对每个资源的双重请求。

一个缓存接收PUT并知道其缓存失效而另一个缓存没有看到它的问题呢?

5 个答案:

答案 0 :(得分:6)

缓存服务器应该在收到PUT时使URI引用的实体无效(但正如您所注意到的,这并未涵盖所有情况)。

除此之外,您可以在响应中使用缓存控制标头来限制或阻止缓存,并尝试处理请求标头,询问自上次提取后URI是否已被修改。

这仍然是一个非常复杂的问题,事实上仍在进行中(例如见http://www.ietf.org/internet-drafts/draft-ietf-httpbis-p6-cache-05.txt

如果内容是加密的(至少使用SSL),代理中的缓存并不真正适用,因此这不应该是一个问题(尽管可能仍然是客户端的问题)。

答案 1 :(得分:2)

HTTP协议支持名为“If-Modified-Since”的请求类型,它基本上允许缓存服务器向Web服务器询问项目是否已更改。 HTTP协议还支持HTTP服务器响应中的“Cache-Control”标头,它告诉缓存服务器如何处理内容(例如从不缓存此内容,或假设它在1天内过期等)。

您还提到了加密回复。 HTTP缓存服务器无法缓存SSL,因为这样做会要求他们将页面解密为“中间人”。这样做在技术上具有挑战性(解密页面,存储它,并为客户端重新加密),并且还会违反页面安全性,导致客户端出现“无效证书”警告。技术上可以让缓存服务器执行此操作,但它会导致比解决的问题更多的问题,这是一个坏主意。我怀疑任何缓存服务器实际上都在做这类事情。

答案 2 :(得分:1)

不幸的是,HTTP缓存基于确切的URI,如果不强制客户端进行缓存重新验证,则无法在您的情况下实现合理的行为。

如果你有:

GET /farm/123
POST /farm_update/123

您可以使用Content-Location标头指定第二个请求修改了第一个请求。 AFAIK你不能用多个URI做到这一点,我还没有检查它是否适用于流行的客户端。

解决方案是让网页快速过期并处理If-Modified-SinceE-Tag状态为304 Not Modified

答案 3 :(得分:1)

你无法缓存动态内容(没有缺点),因为......它是动态的。

答案 4 :(得分:0)

在re:SoapBox的回答中:

  1. 我认为If-Modified-Since是我在问题末尾提出的两阶段GET。这似乎是一个很好的解决方案,其中内容很大(即,在不重新发送内容的情况下,请求数量增加一倍的成本,从而克服了开销。在我的农场示例中,情况并非如此,因为每个农场的信息很短。)

  2. 构建一个通过未加密(HTTP)通道发送加密内容的系统是完全合理的。想象一下面向服务的体系结构的场景,其中更新很少,GET是(a)频繁,(b)需要极快,(c)必须加密。您将构建一个需要FROM标头(或等效地,请求参数中的API密钥)的服务器,并为请求者发回内容的非对称加密版本。非对称加密很慢,但如果正确缓存,则会超过组合SSL握手(非对称加密)和对称内容加密。在此服务器前添加缓存可以大大加快GET s。

  3. 缓存服务器可以在短时间内合理地缓存HTTPS GET。我的银行可能会在我的帐户主页和最近的交易中设置大约5分钟的缓存控制。我不太可能在网站上花费很长时间,所以会话不会很长,而且我可能会在我最近发送的那个检查中找到我的帐户的主页几次到SnorgTees