本文主要介绍缓存的各类类型及在咱们工做中的使用技巧。css
先简单经过几个小例子,来简单描述一下。html
场景1
测试妹子或者是qa小哥哥测功能时会说为何个人浏览器的显示乱七八糟,个人界面怎么跟别人浏览器上不一致?旁边的人会提醒说:清下缓存试试。场景2
开发改了代码,上了环境,发现不生效,这时候首先就是清缓存,清了浏览器缓存发现仍是不行,再检查,发现是反向代理缓存。前端
那么当咱们说web缓存的时候,咱们说的是什么?什么地方能够缓存,何时用什么类型的缓存。若是缓存使用不当的话,又会带来什么样的问题,咱们又该如何去避免? node
其实,缓存就是把数据或者是咱们须要取到的内容,放到能更快访问的地方。缓存对于先后端开发者来讲,咱们使用缓存都是为了可以更好地提高性能。
其中Web缓存就是为了提高Web页面访问的性能,把能缓存的页面或者数据缓存到可以更快取到的地方。nginx
当一个浏览器发起请求时,会通过上图几个步骤,那么缓存的地方其实就是上图中的浏览器、反向代理、cdn、数据库等等。
因此Web 缓存大体能够分为:数据库缓存、服务器端缓存(代理服务器缓存、CDN 缓存)、浏览器缓存。web
数据库缓存是指,当web应用的关系比较复杂,数据库中的表不少的时候,若是频繁进行数据库查询,很容易致使数据库不堪重荷,网站显示延迟等问题。为了提供查询的性能,将查询后的数据放到内存中进行缓存,下次查询时,能够直接从内存缓存返回,提升响应效率,缓存数据库压力。
经常使用的数据库缓存好比配合使用memcache、redis这种高性能的分布式内存缓存服务器。redis
若是使用缓存的话,须要注意 缓存数据和真实数据源数据 的一致性。
服务器缓存主要分为代理服务器缓存和CDN缓存。数据库
代理服务器是浏览器和源服务器之间的中间服务器,浏览器先向这个中间服务器发起Web请求,通过处理后(好比权限验证,缓存匹配等),再将请求转发到源服务器。代理服务器缓存的运做原理跟浏览器的运做原理差很少,只是规模更大。后端
CDN缓存通常是由网站管理员本身部署,为了让他们的网站更容易扩展并得到更好的性能。一般状况下,浏览器先向CDN网关发起Web请求,网关服务器后面对应着一台或多台负载均衡源服务器,会根据它们的负载请求,动态将请求转发到合适的源服务器上。从浏览器角度来看,整个CDN就是一个源服务器,从这个层面来讲,浏览器和服务器之间的缓存机制,在这种架构下一样适用。浏览器
浏览器缓存是全部web应用中都会使用的,每一个浏览器都实现了 HTTP 缓存,咱们经过浏览器使用HTTP协议与服务器交互的时候,浏览器就会根据一套与服务器约定的规则进行缓存工做。咱们能够经过浏览器提供的开发者工具来查看。
浏览器缓存的类型不少,HTTP 缓存、indexDB、cookie、localstorage 等等。
以Chrome浏览器为例,F12打开浏览器开发者工具,再选择“Application”,能够看到全部的缓存类型,以下图所示,咱们能够看到有HTTP文件缓存(Frames下),Local Storage,Session Storage,indexDB,Web SQL,Cookie,CacheStorage,Application Cache等等。
从这张流程图能够看出,浏览器在发送文件请求时,能够根据协议头判断从服务器端请求文件仍是从本地缓存读取文件,影响浏览器的文件缓存主要有几个属性:expires、Etag、Last-Modified,这三个属性是由http协议定义的。
用于设置静态资源的过时时间。它的值一个GMT格式的时间字符串,好比expires:Fri, 27 Jul 2029 13:38:54 GMT
。这个时间表明着这个资源的失效时间,在此时间以前,即命中缓存。这种方式有一个明显的缺点,因为失效时间是一个绝对时间,因此当服务器与客户端时间误差较大时,就会致使缓存混乱。因而发展出了Cache-Control。
Cache-Control是一个相对时间,例如Cache-Control:3600
,表明着资源的有效期是3600秒。因为是相对时间,而且都是与客户端时间比较,因此服务器与客户端时间误差也不会致使问题。能够用于控制是否缓存、缓存的读取权限、资源的有效期。只不过Cache-Control的选择更多,设置更细致,若是同时设置的话,其优先级高于Expires。
Cache-Control能够由多个字段组合而成。下面简单挑几个来讲明:
浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-modify是一个时间标识该资源的最后修改时间,例如last-modified:Fri, 20 Dec 2019 03:34:57 GMT
。
当浏览器再次请求该资源时,当资源过时(使用Cache-Control标识的max-age),发现资源具备Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。
与Last-Modify/If-Modify-Since不一样的是,Etag/If-None-Match返回的是一个校验码(ETag: entity tag)。ETag能够保证每个资源是惟一的,资源变化都会致使ETag变化。ETag值的变动则说明资源状态已经被修改。服务器根据浏览器上发送的If-None-Match值来判断是否命中缓存。
Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的惟一标识(生成规则由服务器定义)。nginx中,etag会默认增长,若是须要关闭,须要在配置文件中设置:etag off;
If-None-Match:当资源过时时(使用Cache-Control标识的max-age),发现资源具备Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。
ETag 扩展说明
咱们对ETag寄予厚望,但愿它对于每个url生成惟一的值,资源变化时ETag也发生变化。神秘的Etag是如何生成的呢?以Apache为例,ETag生成靠如下几种因子
生成Etag的时候,可使用其中一种或几种因子,使用抗碰撞散列函数来生成。因此,理论上ETag也是会重复的,只是几率小到能够忽略。
你可能会以为使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为何还须要Etag(实体标识)呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:
3.有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形
Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的惟一标识符,可以更加准确的控制缓存。Last-Modified与ETag是能够一块儿使用的,服务器会优先验证ETag,一致的状况下,才会继续比对Last-Modified,最后才决定是否返回304。
浏览器的缓存除了和相关的http协议规则有关外,还与用户行为有关。
用户操做 | Expires/Cache-Control | Last-Modified/Etag |
---|---|---|
地址栏回车 | 有效 | 有效 |
页面连接跳转 | 有效 | 有效 |
新开窗口 | 有效 | 有效 |
前进、后退 | 有效 | 有效 |
F5刷新 | 无效 | 有效 |
Ctrl+F5刷新 | 无效 | 无效 |
设置缓存的两种方式:
一、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应用比较适用。