【拿来主义】当我们谈WEB缓存的时候,我们在谈些什么?
第一部分 Web缓存是什么
场景1:测试妹子测功能时会说为什么我的浏览器的显示乱七八糟,我的界面怎么跟别人浏览器上不一致?旁边的人会提醒说:清下缓存试试。
场景2:开发改了代码,上了环境,发现不生效,这时候首先就是清缓存,清了浏览器缓存发现还是不行,再检查,发现是反向代理缓存。
那么,当我们谈WEB缓存的时候,我们说的是什么?什么地方可以缓存?什么时候用什么缓存?使用不当会带来什么问题,我们怎么避免?
会不会傻傻分不清楚,那我们就来理一理,看看web缓存究竟是什么?
缓存:缓存就是把数据或者我们需要取到的内容,放到能更快访问的地方。缓存对于前端后端的coder来说,应该都不陌生,不论前端后端,我们使用缓存都是为了提升性能。
Web缓存:按照上面的逻辑,就是为了提升Web页面访问的性能,把能缓存的页面or数据缓存到能够更快取得的地方。广义的Web缓存也可以包括服务器缓存,本文为了与服务器缓存区分,不包含服务器缓存。
第二部分 Web缓存的类型
在典型的web应用中,一个浏览器发起的请求,会经过下图中的几个步骤(其中CDN、反向代理是可选的),那么缓存的地方或者层次也很好理解,就是下图中的浏览器、反向代理、cdn。
第三部分 浏览器缓存
浏览器缓存是所有WEB应用都会使用的,浏览器的缓存类型很多,我们可以通过浏览器提供的开发者工具来查看。
以chrome浏览器为例,打开chrome开发者工具,再选择“Resources”中看到所有的缓存类型,如下图所示:
一、Frames
Frames的缓存,是基于HTTP协议的浏览器文件级缓存。
浏览器在发送文件请求时,可以根据协议头判断从服务器端请求文件还是从本地缓存读取文件,主要判断依据是expires和etag,读取文件的流程如下图:
从这张流程图可以看出,影响浏览器的文件缓存主要有几个属性:expires、Etag、Last-Modified,这三个属性是由http协议定义的。
(一)控制缓存的属性
在http1.0中约定用expires来确定是否使用缓存中的文件。http1.1中约定使用的是Cache-Control、Last-Modified/If-Modified-Since、etag。下面来分别看下各种属性的定义:
1、Expires
用于设置静态资源的过期时间。
2、Cache-Control
Cache-Control可以用于控制是否缓存、缓存的读取权限、资源的有效期。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires。
(1)public 指示响应数据可以被任何客户端缓存
(2)private 指示响应数据可以被非共享缓存所缓存。这表明响应的数据可以被发送请求的浏览器缓存,而不能被中介所缓存
(3)no-cache 指示响应数据不能被任何接受响应的客户端所缓存
(4)no-store 指示所传送的响应数据除了不能被缓存,也不能存入磁盘。一般用于敏感数据,以免数据被复制。
(5)must-revalidate 指示所有的缓存都必须重新验证,在这个过程中,浏览器会发送一个If-Modified-Since头。如果服务器程序验证得出当前的响应数据为最新的数 据,那么服务器应当返回一个304 Not Modified响应给客户端,否则响应数据将再次被发送到客户端。
(6)proxy-revalidate 与must-revalidate相似,不同的是用来指示共享缓存。
(7)max-age:(单位秒) 数据经过max-age设置的秒数后就会失效,相当于HTTP/1.0中的Expires头。如果在一次响应中同时设置了max-age和Expires,那么max-age将具有较高的优先级。(注:ngnix设置expires会被转换为max-age)
3、Last-Modified/If-Modified-Since
l Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。
l If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。
4、Etag/If-None-Match
Etag/If-None-Match也要配合Cache-Control使用。
Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器定义)。nginx中,etag会默认增加,如果需要关闭,需要在配置文件中设置:etag off;
l If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。
(二)用户行为与缓存
浏览器缓存行为还有用户的行为有关
用户操作 | Expires/Cache-Control | Last-Modified/Etag |
地址栏回车 | 有效 | 有效 |
页面链接跳转 | 有效 | 有效 |
新开窗口 | 有效 | 有效 |
前进、后退 | 有效 | 有效 |
F5刷新 | 无效 | 有效 |
Ctrl+F5刷新 | 无效 | 无效 |
(二)如何控制缓存
设置缓存的两种方式:
1、web服务器配置
以ngnix为例,在nginx.conf中设置:
location~ .*\.(gif|jpg|png|htm|html|css|js|flv|ico|swf)(.*) {
expires 1d;
}
上述配置表示这些静态文件1天后过期。如果想配置为完全不缓存,那么可以设置为expires -1;(后面的数字配置为负数),返回的header会被设置为Cache-Control:no-cache
2、后台代码写入
例如:
response.setHeader("Cache-Control", "no-cache");
3、html 的meta标签
<meta http-equiv="Cache-Control" content="max-age=7200" />
(三)缓存的问题和解决办法
1、引入缓存之后,主要有两个问题:
(1)浏览器不知道有资源更新,还是使用缓存中的老文件。
(2)各个文件缓存策略不一致,有关联关系的文件,有的从服务器加载,有的直接取浏览器缓存的,这样有可能会导致界面混乱。
2、解决方式
(1)Etag或Last-modified
Etag是服务端根据文件信息生成的字符串,当服务端文件更新时,Etag也会变化,这样能保证当服务端文件更新时,取到新的文件内容。
但是Etag这种解决方式的问题是,请求还是会发到服务端,由服务端进行判断。
Last-modified与Etag类似。
(2)文件名后缀
构建过程中,把构建生成的文件加上随机后缀,主入口html中的引用文件在构建中替换为增加了文件名后缀的;主入口文件配置为不缓存。
当服务端更新文件时,由于文件名后缀更改,浏览器缓存匹配不上,会直接到服务端获取,服务端没有更新文件时,在浏览器缓存获取。
这种方式效果较好,但是需要引入构建,对于已经使用了前端构建的web应用比较适用。
二、cookie
cookie是一种能够让网站服务器把少量数据储存到客户端的硬盘或内存,或是从客户端的硬盘读取数据的一种技术。当我们浏览某网站时,由Web服务器置于你硬盘上的一个非常小的文本文件,它可以记录用户ID、密码、浏览过的网页、停留的时间等信息。
cookie以键值对的方式来存储,有数量和大小的限制,数量各个浏览器不同,大小不能超过4K。
(一)设置cookie的方式:
1、浏览器
浏览器提供了操作cookie的方式,可以对cookie进行设置、读取、删除。另外,cookie也可以设置过期时间。
浏览器获取cookie的方式:
document.cookie
2、服务器
很多时候,我们会使用cookie来做协助做会话管理,登录成功后,由服务端将sessionid信息写入cookie,后续客户端发送的所有请求都携带cookie信息,服务端验证cookie中的sessionid信息,判断此请求是否合法。以java为例,服务端写入cookie的方法如下:
Cookie cookie = new Cookie("sessionid",URLEncoder.encode("fejerwiie2234","UTF-8")); response.addCookie(cookie);
(二)cookie的属性
属性名称 | 属性含义 |
name | cookie的名称 |
value | cookie的值 |
domain | 可以访问此cookie的域名 |
path | 可以访问此cookie的页面路径。 如domain是abc.com,path是/test,那么只有/test路径下的页面可读取此cookie |
expires/Max-Age | 字段为此cookie超时时间。若设置其值为一个时间,那么当到达此时间后,此cookie失效。不设置的话默认值是Session,意思是cookie会和session一起失效。当浏览器关闭(不是浏览器标签页,而是整个浏览器) 后,此cookie失效 |
Size | 此cookie大小 |
httponly | cookie的httponly属性。若此属性为true,则只有在http请求头中会带有此cookie的信息,而不能通过document.cookie来访问此cookie。 |
secure | 设置是否只能通过https来传递此条cookie |
三、localStorage
localstorage是Html5中新加入的特性,引入localstorage主要是作为浏览器本地存储,解决cookie作为存储容量不足的问题。localstorage是一种持久化的存储。
同样,localstorage也是一个key-value形式的存储。
(一)浏览器提供了localstorage的增删改查方法
增加/修改:window.localStorage.setItem("username","admin");
查询:window.localStorage.getItem("username");
删除:window.localStorage.removeItem("username","admin");
(二)使用localstorage的注意事项
localstorage中存储的value只能是字符串型,如果要存储对象,需要转换为字符串再进行保存。
四、sessionStorage
sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储。
同样,浏览器也提供了sessionStorage的增删改查方法,与localStorage一致,只是获取方法为:window.sessionStorage。
五、IndexedDB
IndexedDB也是html5提供的,能够在客户端存储大量的结构化数据的数据库,并且提供API进行高效检索。IndexedDB的初始大小是50M,还可以增加,就存储量来说,秒杀其他存储方式。
但是它的缺点也很明显,IndexedDB并不是所有主流浏览器都支持,比如IE9、IE10和IE11都不支持,所以,如果你的用户群还使用着IE系列的浏览器,IndexedDB就不用考虑了。
IndexedDB也有一些api,这里不再详述了,可以参考:
https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API
六、Web SQL
此方案在W3C已经废弃,不再维护,替代方案是IndexedDB。
七、application cache
该特性已经从 Web 标准中删除。
八、Cache Storage
该方案是一个实验性的方案,并不是所有浏览器都支持。
CacheStorage是在ServiceWorker的规范中定义的。CacheStorage 可以保存每个serverWorker申明的cache对象,cacheStorage有open、match、has、delete、keys五个核心方法,可以对cache对象的不同匹配进行不同的响应。
九、Services Worker
service worker提供了很多新的能力,使得web app拥有与native app相同的离线体验、消息推送体验。service worker也是一个实验性的方案,并不是所有浏览器都支持。
Service worker可以:
-
后台消息传递
-
网络代理,转发请求,伪造响应
-
离线缓存
-
消息推送
可以参考:https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers
第三部分、CDN缓存
网站的加载速度,除了资源的多少和大小外,很大部分时间是用于网络传输的,而网络传输时间与用户浏览器与资源所在服务器的地理位置直接相关,要提升网站加载速度,一个办法就是使资源所在服务器与用户的地理位置尽量靠近。
CDN:全称是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。CDN包括分布式存储、负载均衡、网络请求的重定向和内容管理4个要件。而其中呢,内容管理和全局的网络流量管理是CDN的核心所在。CDN确保内容以一种极为高效的方式为用户的请求提供服务,使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。
CDN的拓扑图如下:
一、CDN的缓存机制
CDN边缘节点缓存策略因服务商不同而不同,但一般都会遵循http标准协议,通过http响应头中的Cache-control: max-age的字段来设置CDN边缘节点数据缓存时间。当客户端向CDN节点请求数据时,CDN节点会判断缓存数据是否过期,若缓存数据并没有过期,则直接将缓存数据返回给客户端;否则,CDN节点就会向源站发出回源请求,从源站拉取最新数据,更新本地缓存,并将最新数据返回给客户端。所以,如果我们修改了内容,最好加个版本号,让CDN重新获取资源,从而减少不必要的麻烦。
CDN服务商一般会提供基于文件后缀、目录多个维度来指定CDN缓存时间,为用户提供更精细化的缓存管理。CDN缓存时间会对“回源率”产生直接的影响。若CDN缓存时间较短,CDN边缘节点上的数据会经常失效,导致频繁回源,增加了源站的负载,同时也增大的访问延时;若CDN缓存时间太长,会带来数据更新时间慢的问题。开发者需要增对特定的业务,来做特定的数据缓存时间管理。
二、CDN的问题
CDN的分流作用不仅减少了用户的访问延时,也减少了源站的负载。
但其缺点主要是缓存的同步问题:当网站更新时,如果CDN节点上数据没有及时更新,即便用户再浏览器使用Ctrl +F5的方式使浏览器端的缓存失效,也会因为CDN边缘节点没有同步最新数据而导致用户访问异常。
三、如何解决CDN的问题
CDN的主要问题是由于缓存同步不及时带来的,缓存更新有两种方式:
(一)定制缓存策略
静态文件在返回时由源服务器控制expires、cache-control等属性来定义CDN的缓存策略。
(二)源服务器资源更新时,主动刷新CDN缓存
CDN边缘节点对开发者是透明的,相比于浏览器Ctrl+F5的强制刷新来使浏览器本地缓存失效,开发者可以通过CDN服务商提供的“刷新缓存”接口来达到清理CDN边缘节点缓存的目的。这样开发者在更新数据后,可以使用“刷新缓存”功能来强制CDN节点上的数据缓存过期,保证客户端在访问时,拉取到最新的数据。
第四部分、反向代理缓存
反向代理(Reverse Proxy): 这种机制是Web服务器隐藏在代理服务器之后,实现这种机制的服务器称作反向代理服务器(Reverse Proxy Server)。此时,Web服务器成为后端服务器,反向代理服务器称为前端服务器。
引入反向代理服务器的目的之一就是基于缓存的加速。我们可以将内容缓存在反向代理服务器上,所有缓存机制的实现仍然采用HTTP/1.1协议。
(一)反向代理缓存配置
以通常使用的反向代理--ngnix为例,实现缓存的配置如下:
1、proxy_cache_path
语法:proxy_cache_path path [levels=number] keys_zone=zone_name:zone_size [inactive=time] [max_size=size];
默认值:None
使用字段:http
指令指定缓存的路径和一些其他参数,缓存的数据存储在文件中,并且使用代理url的哈希值作为关键字与文件名。levels参数指定缓存的子目录数,例如:
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m;
文件名类似于:
/data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c
levels指定目录结构,可以使用任意的1位或2位数字作为目录结构,如 X, X:X,或X:X:X 例如: “2”, “2:2”, “1:1:2“,但是最多只能是三级目录。
2、proxy_cache
语法:proxy_cache zone_name;
默认值:None
使用字段:http, server, location
设置一个缓存区域的名称,一个相同的区域可以在不同的地方使用。
3、proxy_cache_valid
语法:proxy_cache_valid reply_code [reply_code …] time;
默认值:None
使用字段:http, server, location
为不同的应答设置不同的缓存时间,例如:
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
为应答代码为200和302的设置缓存时间为10分钟,404代码缓存1分钟。
如果只定义时间:
proxy_cache_valid 5m;
那么只对代码为200, 301和302的应答进行缓存。
同样可以使用any参数任何应答。
proxy_cache_valid 200 302 10m;
proxy_cache_valid 301 1h;
proxy_cache_valid any 1m;
参考地址不明