如何快速更新长缓存的 HTTP 资源
前言
HTTP 缓存时间一直让开发者头疼。时间太短,性能不够好;时间太长,更新不及时。当遇到严重问题需紧急修复时,尽管后端文件可快速替换,但前端文件仍从本地缓存加载,导致更新长时间无法生效。
对于这个问题,很多网站都有相应的解决方案。
传统方案
最常见的方案,就是为静态资源设置很长的缓存时间,然后在文件名中加入版本号或 Hash 值等字符,这样文件更新后 URL 会变化,即可避开之前版本的缓存。
不过这种方案也有缺陷。假设网页中有如下依赖关系的文件:
HTML
└─JS
└─CSS
└─GIF
现在需紧急更新 GIF 文件,于是重新发布 GIF 得到新的 URL。然而 CSS 中引用的仍是之前的 GIF URL,因此 CSS 也要修改和发布,从而得到新的 CSS URL,进而导致加载该 CSS 的 JS 也得更新,加载该 JS 的 HTML 也得更新。
由此可见,快速更新一个文件需更新整个依赖链,白白浪费不少流量。同时产生大量无意义的新版本文件,它们的业务功能并无变化,只是为了修改其中的 URL 而已。
如果能通过 JS 删除某个文件的 HTTP 缓存,这样就不用更换 URL 了,更不必修改其他文件。是否有这样的黑科技?
高级方案
现代浏览器添加了很多新特性,其中有些和缓存相关。
例如 Clear-Site-Data 特性。当 HTTP 存在 Clear-Site-Data: "cache"
响应头时,即可删除该站点的缓存文件。不过它会删除所有文件,而无法指定文件,因此不适合本案例。
例如 Fetch API 中 Request 对象的 cache 属性也涉及缓存操作。阅读文档,其中 no-cache
非常有趣:
no-cache
— The browser looks for a matching request in its HTTP cache.
- If there is a match, fresh or stale, the browser will make a conditional request to the remote server. If the server indicates that the resource has not changed, it will be returned from the cache. Otherwise the resource will be downloaded from the server and the cache will be updated.
- If there is no match, the browser will make a normal request, and will update the cache with the downloaded resource.
我们先看第一段:如果请求的文件存在缓存,无论是否过期,浏览器都会和后端协商缓存。未变化则使用本地缓存(HTTP 304),有变化则重新下载并 更新缓存。
虽然无法删除 HTTP 缓存,但能更新缓存内容,也是不错的。
演示
基于该特性,这里做了个演示:https://www.etherdream.com/cache-purge/
该页面引用了 res.js ,其内容每隔 5s 递增,可通过页面中 Res Ver
显示。
由于 res.js
设置了很长的缓存时间,因此不断刷新页面 Res Ver
也不会变化。(Chrome 刷新时只有页面走协商缓存,内部资源仍从本地缓存加载)
点击 Purge
按钮,再次刷新页面,这时 Res Ver
变化了。之后不断刷新,数字仍保持不变。
通过控制台可见,no-cache
请求不会直接使用本地缓存,并且返回的内容会覆盖本地缓存。从而实现 HTTP 长缓存资源也能热更新!
如果 5s 内再次点击 purge,文件不会重新下载,而是协商返回 304 状态,符合文档中的描述。
应用
这个特性如何应用到实际业务中?首先看下兼容性:
目前绝大多数浏览器都支持。如果不考虑低版本浏览器,那该如何使用?
显然需要一个清单列表,记录哪些文件需热更新。同时为了防止重复更新,还需记录每个文件的版本号:
/foo.gif 100
/bar.gif 101
...
热更新后将 URL 和版本号记录到本地存储中。之后再次执行时,如果版本号和本地存储中的相同,就不必再更新了。
至于什么时候执行,最简单的当然是页面打开时执行,但这不是最好的,因为:
每次打开页面都要加载清单文件,增加请求
热更新需要一定的时间(包括加载清单),页面初始化时不会等你,它仍使用现有的缓存资源
页面运行时定期执行,可解决第 2 个问题,从而在下次访问页面之前完成更新,但轮询清单会带来更多请求。
如需减少请求,可使用服务端推送技术,当清单变化时主动推送给前端。这样不仅可减少请求,而且前端能在第一时间更新。
缺陷
不过这个方案仍有缺陷。我们的初衷是删除缓存,等业务再次使用时才下载;而本方案却是更新缓存,提前将文件下载到本地,附带预加载的功能。
如果热更新的是常用的公共文件倒还好,但如果是小部分用户才会用到该文件,却让所有用户都提前预加载,这显然不合理,反而更浪费流量。
终极方案
即使不能操作 HTTP 缓存,但如果能拦截 HTTP 请求并返回自定义内容,同样可达到热更新的效果。这正是 Service Worker API 的设计初衷。
开发者只需提供一个清单,将原始 URL 对应到最新 URL:
/foo.gif
https://cdn.mysite.com/1.0.0/foo.gif
/bar.gif
https://cdn.mysite.com/1.0.1/bar.gif
Service Worker 根据清单中的地址反向代理。这样,只需更新一个清单文件,即可更新所有 URL 的内容。
得益于 Service Worker 能在浏览器后台持续运行,因此其创建的 WebSocket 可长时间保持连接状态,结合后端的更新推送服务,可大幅减少加载清单的开销。
基于这个功能实现的演示:https://github.com/EtherDream/freecdn/tree/master/examples/quick-update
如何快速更新长缓存的 HTTP 资源的更多相关文章
- 如何热更新长缓存的 HTTP 资源
前言 HTTP 缓存时间一直让开发者头疼.时间太短,性能不够好:时间太长,更新不及时.当遇到严重问题需紧急修复时,尽管后端文件可快速替换,但前端文件仍从本地缓存加载,导致更新长时间无法生效. 对于这个 ...
- [.NET] 《Effective C#》快速笔记(二)- .NET 资源托管
<Effective C#>快速笔记(二)- .NET 资源托管 简介 续 <Effective C#>读书笔记(一)- C# 语言习惯. .NET 中,GC 会帮助我们管理内 ...
- Redis系列十:缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级
一.缓存雪崩 缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而 ...
- SpringMVC的缓存对静态资源的影响 304 Not Modified
我们知道在springmvc的配置中,可以添加缓存,但是缓存到底对静态资源有什么影响? 测试 没有添加缓存 <mvc:resources mapping="/image/**" ...
- 《Effective C#》快速笔记(二)- .NET 资源托管
简介 续 <Effective C#>读书笔记(一)- C# 语言习惯. .NET 中,GC 会帮助我们管理内存,我们并不需要去担心内存泄漏,资源分配和指针初始化等问题.不过,它也并非万能 ...
- 增强织梦DedeCMS“更新系统缓存”清理沉余缓存的功能
我们使用织梦DedeCMS系统有很长一段时间后,不间断的在后台更新系统缓存的时候,有些缓存文件夹及缓存文件没有被清理,导致日积月累的垃圾缓存文件越来越多,可以以百千万计算,现在增强更新系统缓存功能清理 ...
- Redis之缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级
目录 Redis之缓存雪崩.缓存穿透.缓存预热.缓存更新.缓存降级 1.缓存雪崩 2.缓存穿透 3.缓存预热 4.缓存更新 5.缓存降级 Redis之缓存雪崩.缓存穿透.缓存预热.缓存更新.缓存降级 ...
- winform快速开发平台->让有限的资源创造无限的价值!
最近一直在维护一套自己的快速开发平台. 主要应对针对C/S架构下的项目.然而对winform这快,还真没有看到过相对好的快速开发平台, 何为快速,在博客园逛了了好久, 预览了很多通用权限管理系统. 确 ...
- 更新页面缓存OutputCache
为什么要使用OutputCache 我认为OutputCache是最简单的缓存技术了,它针对的是页面级别的,简单的一条指令就可以达到缓存的效果,有效的减轻服务器的压力和减少带宽,对于网站一些不会频繁更 ...
随机推荐
- Mobileye独创性创新
Mobileye独创性创新 尽管存在相似之处,但Nvidia的SFF无法与Mobileye的RSS相匹配,后者是领先的AV安全模型 迈向无人驾驶的未来,Mobileye继续以新的创新引领行业,不仅将使 ...
- NSight Compute 用户手册(上)
NSight Compute 用户手册(上) 非交互式配置文件活动 从NVIDIA Nsight Compute启动目标应用程序 启动NVIDIA Nsight Compute时,将出现欢迎页面.单击 ...
- 保护嵌入式802.11 Wi-Fi设备时需要考虑的10件事
保护嵌入式802.11 Wi-Fi设备时需要考虑的10件事 10 things to consider when securing an embedded 802.11 Wi-Fi device 随着 ...
- springMVC异常处理(自定义异常)HandlerExceptionResolver
注:本篇的异常主要指的是controller.service和dao层中执行方法抛出的异常. 一.为什么要处理异常? 因为如果我们不处理异常,异常信息就会直接抛出给浏览器,于是浏览器页面就直接显示异常 ...
- 十、构建memcached服务
装包-------配置----起服务---验证 装包: [root@proxy ~]# yum -y install memcached //安装软件包memcached [root@ ...
- 【NX二次开发】Block UI 指定坐标系
属性说明 属性 类型 描述 常规 BlockID String 控件ID Enable Logical 是否可操作 Group ...
- 「10.11」chess(DP,组合数学)·array(单调栈)·ants(莫队,并茶几)
菜鸡wwb因为想不出口胡题所以来写题解了 A. chess 昨天晚上考试,有点困 开考先花五分钟扫了一边题,好开始肝$T1$ 看了一眼$m$的范围很大,第一反应矩阵快速幂?? $n$很小,那么可以打$ ...
- iOS-block本质是什么?
一: block的原理是怎样的?本质是什么? block本质上也是一个OC对象,因为它的内部也有个isa指针 block是封装了函数调用以及函数调用环境的OC对象 接下来我们将通过底层源码来论证上诉两 ...
- 『心善渊』Selenium3.0基础 — 9、使用Seleniun中的By类定位元素
目录 1.使用By定位的前提 2.By定位的方法 3.By定位的使用 4.复数形式的示例 我们还可以通过Seleniun测试框架中的By类,来实现页面中的元素定位. 1.使用By定位的前提 需要导入B ...
- Centos7一键配置阿里云yum源脚本
Centos7一键配置阿里云yum源脚本 工作中linux系统经常要配置网络yum,故写了一个简单的配置阿里云yum源的的脚本可以单独使用也可以在做自动化部署的时候调用. #!/bin/bash # ...