I';我对HTTP缓存感到困惑

I';我对HTTP缓存感到困惑,http,rest,caching,Http,Rest,Caching,我一直在考虑在RESTful环境中进行批读和批写,我想我已经意识到我对HTTP缓存有更广泛的问题。(下面我使用逗号(“,”)来分隔多个记录ID,但这一细节并不是讨论的重点。) 我从这个问题开始: 1.批量更新使单个GET无效 客户端和农场服务器之间的缓存服务器在看到放置时如何知道使其/Farms/123缓存无效 然后我意识到这也是一个问题: 2.批GET通过单个(或批)更新无效 当缓存看到PUT经过时,它如何知道使多场GET无效 所以我认为问题其实只是批处理操作。然后我意识到任何关系都可能导致类

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

我从这个问题开始:

1.批量更新使单个
GET
无效 客户端和农场服务器之间的缓存服务器在看到
放置
时如何知道使其
/Farms/123
缓存无效

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

2.批
GET
通过单个(或批)更新无效 当缓存看到PUT经过时,它如何知道使多场
GET
无效

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

3.单个
GET
因更新相关记录而无效 当缓存看到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)的内容:

第二个GET应该返回如下内容:

farm: {
  id: 123,
  name: "Shady Acres",
  size: "60 acres",
  farmer_id: null
}
但它不能!即使您正确地使用了
ETag
s,也不行。您不能期望缓存服务器检查
ETag
s的内容——这些内容可能是加密的。而且您不能期望服务器通知缓存记录应该失效——缓存不会向服务器注册它们自己

那么有没有我遗漏的标题?指示缓存应该在对某些资源执行任何
GET
s之前执行
HEAD
?如果我能告诉缓存哪些资源可能会频繁更新,我想我可以接受对每个资源的双重请求


那么一个缓存接收到
PUT
并知道使其缓存无效,而另一个缓存没有看到它的问题又如何呢?

HTTP协议支持一种称为“If Modified-Since”的请求类型,它基本上允许缓存服务器询问web服务器项目是否已更改。HTTP协议还支持HTTP服务器响应中的“缓存控制”头,这些头告诉缓存服务器如何处理内容(例如从不缓存该内容,或者假设它在1天内过期,等等)


您还提到了加密响应。HTTP缓存服务器无法缓存SSL,因为这样做会要求它们将页面解密为“中间人”。这样做将是技术上具有挑战性的(对页面进行解密、存储并为客户端重新加密),并且还会违反页面安全性,导致客户端上的“无效证书”警告。从技术上讲,让缓存服务器来做这件事是可能的,但它造成的问题比解决的问题多,这是一个坏主意。我怀疑是否有任何缓存服务器真的会做这种事情。

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

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

这仍然是一个非常复杂的问题,事实上仍在研究中(例如,请参阅)


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

不幸的是,HTTP缓存基于确切的URI,如果不强制客户端进行缓存重新验证,您就无法实现明智的行为

如果您有:

GET /farm/123
POST /farm_update/123
您可以使用
contentlocation
头指定第二个请求修改了第一个请求。好吧,你不能用多个URI来实现这一点,我还没有检查过这在流行客户端中是否有效

解决方案是使页面快速过期,并在修改后处理
,或
E-Tag
状态为
304未修改的情况下处理

在回复中:

  • 我认为
    如果修改,因为
    是我在问题末尾建议的两阶段
    GET
    。在内容较大的情况下,这似乎是一个不错的解决方案(即请求数量翻倍的成本,因此不重新发送内容的收益克服了开销。在我的农场示例中,情况并非如此,因为每个农场的信息都很短。)

  • 构建一个通过未加密(HTTP)通道发送加密内容的系统是完全合理的。想象一下,在面向服务的体系结构中,更新很少,而
    GET
    s(a)频繁,(b)需要非常快,并且(c)必须加密。您将构建一个服务器,该服务器需要一个
    FROM
    头(或者,相当于请求参数中的API密钥),并向请求者发回内容的非对称加密版本。非对称加密的速度很慢,但如果缓存得当,它将胜过SSL握手(非对称加密)和对称内容加密的组合。在此服务器前面添加缓存将显著加快
    GET
    s

  • 缓存服务器可以在短时间内合理地缓存HTTPS GET。我的银行可能会在我的账户主页和最近的交易上设置大约5分钟的缓存控制。我不太可能在网站上花费很长时间,因此会话不会很长,而且我可能会在寻找我最近发送的支票时多次访问我帐户的主页


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

    ori
    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
    
    farm: {
      id: 123,
      name: "Shady Acres",
      size: "60 acres",
      farmer_id: 987
    }
    
    farm: {
      id: 123,
      name: "Shady Acres",
      size: "60 acres",
      farmer_id: null
    }
    
    GET /farm/123
    POST /farm_update/123