使用浏览器缓存进行增量更新

时间:2017-05-26 03:07:30

标签: ajax rest http caching single-page-application

客户端(AngularJS应用程序)从服务器获取相当大的列表。这些列表可能包含数百或数千个元素,这可能意味着几兆字节未压缩(并且一些用户(管理员)可以获得更多数据)。

我不打算让客户端得到部分结果,因为排序和过滤不应该打扰服务器。

压缩工作正常(因数大约为10),并且由于列表不经常更改,304 NOT MODIFIED也有很大帮助。但是缺少另一个重要的优化:

由于列表的典型更改相当小(例如,修改两个元素并添加新元素),因此传输更改听起来只是一个好主意。我想知道如何正确地做到这一点。

GET /offer/123/items之类的内容应该始终返回商品号码123中的所有商品,对吗?这里可以使用压缩和304,但没有增量更新。像GET /offer/123/items?since=1495765733这样的请求听起来像是要走的路,但是浏览器缓存不会被使用:

  • 要么没有任何改变,答案是空的(缓存没有意义)
  • 或者有些东西发生了变化,客户端更新了它的状态,并且从不再要求自1495765733以来的更改(并且缓存它更不合理)

显然,当使用“since”查询时,“资源”不会缓存任何内容(原始查询只使用一次或根本不使用)。

所以我不能依赖浏览器缓存,我只能使用localStoragesessionStorage,这有一些缺点:

  • 它限制在几兆字节(浏览器HTTP缓存可能会大得多并自动处理)
  • 当我达到极限时,我必须实施一些替换策略
  • 浏览器缓存存储了我没有得到的压缩数据(我必须重新压缩它们)
  • 它不适用于用户(管理员)获取更大的列表,因为即使单个列表可能已超出限制
  • 它在退出时被清空(客户的要求)

鉴于有HTML 5和HTTP 2.0,这是非常令人不满意的。我错过了什么?

是否可以将浏览器HTTP缓存与增量更新一起使用?

3 个答案:

答案 0 :(得分:4)

我认为你缺少一件事:总之,标题。我认为你可以做的并且符合(大多数)你的要求的是:

  • 首先GET /offer/123/items正常完成,没什么特别的。
  • 子句GET /offer/123/items将与Fetched-At: 1495765733标头一起发送,表示您的服务器何时发送了初始请求。

从现在开始,可能有两种情况。

  • 要么没有变化,你可以发送304。
  • 如果有更改,请返回新项目,因为之前发送的时间戳包含标题,但会从您的回复中设置Cache-Control: no-cache

这使您可以进行增量更新,并缓存初始兆字节大小的元素。

但仍有一个缺点,即缓存只执行一次,它不会缓存更新。你说你的列表不经常更新,所以它可能已经适合你了,但是如果你真的想进一步推动它,我可以想到一件事。

收到增量更新后,您可以在后台触发另一个请求,而不会使用您的应用程序根本不会使用的Fetched-At标头,但只会更新您的http缓存。它不应该像听起来那么糟糕,因为你的框架不会用新的数据更新它的数据(并且可能触发重新渲染),唯一值得注意的缺点是网络和内存消耗。在移动设备上它可能会有问题,但它听起来并不像是要在它们上显示的应用程序。

我绝对不知道你的用例,只会把它扔出去,但你真的确定做某种分页是行不通的吗?对于普通人来说,数兆字节的数据听起来很多;)

答案 1 :(得分:3)

我会完全放弃请求/响应周期并转移到推送模型。 具体来说, WebSockets

这是用于提供实时股票数据表的金融交易网站的标准技术。这是一个展示WebSockets功能的生产应用程序:

https://www.poloniex.com/exchange#btc_eth

WebSocket应用程序有两种类型的状态:全局和用户。上面的链接将显示三个全球数据表。当您登录时,底部会显示两个用户数据附加表。

这不是HTTP;你将无法将其打成Java Servlet。您需要在服务器上运行单独的进程,该进程通过TCP进行通信。好消息是,有成熟的解决方案随时可用。基于Java的解决方案具有非常好的免费许可选项,包括客户端和服务器API(并且与Angular2集成)是Lightstreamer。他们也有一个组织良好的demo page。还有适配器可用于与您的数据源集成。

您可能会犹豫是否放弃现有的servlet方法,但从长远来看,这将不那么令人头疼,并且可能会非常精彩。即使使用精心设计的仅限标头请求,HTTP轮询也不能很好地扩展到经常更新的大型列表。

---------- 编辑 ----------

由于列表更新很少,因此WebSockets可能过度。根据对此答案的评论提供的进一步细节,我建议使用基于DOM的,AJAX更新的分拣机和过滤器,例如DataTables,它有一些内置的options for caching。为了跨会话重用客户端数据,应修改上一个链接中的ajax请求,以便在每个ajax请求之后将current data in the table保存到localStorage,并在客户端启动新会话时,使用这个数据。这将允许插件管理过滤,排序,缓存和基于浏览器的持久性。

答案 2 :(得分:2)

我正在考虑类似于Aperçu的想法,但使用了两个请求。这个想法还不完整,所以请耐心等待......

  • 客户端要求GET /offer/123/items,可能包含ETagFetched-At标题。

服务器以

回答
  • 200以及完整列表,如果缺少任何标头,或者自Fetched-At时间戳
  • 以来有太多更改
  • 304如果此后没有任何变化
  • 304以及一个特殊的Fetch-More标题告诉客户端要提取更多数据

最后一种情况违反了HTTP应该如何工作,但AFAIK是让浏览器缓存所有我想要它缓存的唯一方法。由于整个通信都是加密的,代理人不能因违反规范而惩罚我。

客户通过请求Fetch-Errata GET来回应/offer/123/items/errata。这样,资源就分成了两个请求。分裂是丑陋的,但角度$ http拦截器可以隐藏应用程序的丑陋。

第二个请求也是可缓存的,并且还可以有Fetched-At标头。细节尚不清楚,但一些强有力的手淫让我相信它可以奏效。实际上,勘误本身可能是不准确的,但仍然有用并且自己获得勘误......等等。

使用HTTP / 1.1,更多的请求可能意味着更多的延迟,但由于节省的带宽,其中一些请求仍然有利可图。服务器可以决定何时停止。

使用HTTP / 2,可以一次发送多个请求。可以使服务器有效地处理它们,因为它知道它们属于一起。一些更多的手淫......

我觉得这个想法很奇怪,但很有趣,我很期待发表评论。请随意给我打电话,但请留下解释。