缓存那些事

 

 

首先我画了一个粗糙的图形,用来表示咱们使用web请求所经历的过程。你们将就看下html

从图能够看出,当用户发起一个网络请求。首先会通过浏览器的缓存,而后通过静态资源的CDN缓存,而后是Nginx等反向代理缓存,再是web缓存,最后还有数据库缓存。下面咱们一一来看。前端

一.Browser Cache

http协议有本身的缓存协商机制。 react

对于http1.0 使用的是基于iso-time的   expired响应头去判断资源过时与否。  webpack

http1.1使用三种web

1.是基于修改精确时间的Last-Modifieredis

2基于文件相对时间的max-age(这个资源你能够用多久)spring

from cache 是没有和服务器确认,通常是运维去除了e-tag或者说是使用了长缓存数据库

3.基于文件内容hash的 e-tag浏览器

 

另一方面,能够把缓存分红三种策略,分别是存储策略,协商策略和过时策略。缓存

last-modify , eTag属于协商策略,协商策略用于从新验证缓存资源是否有效, 须要发一次http请求,用于判断当前的缓存资源是否有效。 强缓存只有cache-control(http1.1)  和  pragma(http1.0)的 max-age等策略命中。 也就是说last-modify , eTag 这种协商缓存并不会减小http请求,而是减小了请求时间和字节数。

对于http1.0 的 Expires   实际上是过时策略,用于判断当前浏览器存储的缓存是否过时,Pragma指定缓存机制.    http1.0 已经淘汰,仅供了解

cache-control 指定缓存机制,须要单独看待,他能够覆盖其余header的值,何解?

语法为: "Cache-Control : cache-directive".

Cache-directive共有以下12种(其中请求中指令7种, 响应中指令9种):

Cache-directive 描述 存储策略 过时策略 请求字段 响应字段
public 资源将被客户端和代理服务器缓存 ✔️     ✔️
private 资源仅被客户端缓存, 代理服务器不缓存 ✔️     ✔️
no-store 请求和响应都不缓存 ✔️   ✔️ ✔️
no-cache 至关于max-age:0,must-revalidate即资源被缓存, 可是缓存马上过时, 同时下次访问时强制验证资源有效性 ✔️ ✔️ ✔️ ✔️
max-age 缓存资源, 可是在指定时间(单位为秒)后缓存过时 ✔️ ✔️ ✔️ ✔️
s-maxage 同上, 依赖public设置, 覆盖max-age, 且只在代理服务器上有效. ✔️ ✔️   ✔️
max-stale 指定时间内, 即便缓存过期, 资源依然有效   ✔️ ✔️  
min-fresh 缓存的资源至少要保持指定时间的新鲜期   ✔️ ✔️  
must-revalidation /proxy-revalidation 若是缓存失效, 强制从新向服务器(或代理)发起验证(由于max-stale等字段可能改变缓存的失效时间)   ✔️   ✔️
only-if-cached 仅仅返回已经缓存的资源, 不访问网络, 若无缓存则返回504     ✔️  
no-transform 强制要求代理服务器不要对资源进行转换, 禁止代理服务器对Content-Encoding,Content-Range,Content-Type字段的修改(所以代理的gzip压缩将不被容许)     ✔️ ✔️

二.CDN Cache   

CND是为了避免同的网络环境均可以得到一致的高速体验而服务的。将一些静态资源放在cdn可使用户访问这个静态资源的时候选择网络状态最好的。合理使用cdn能够大大提升访问速度。可是cdn也有很麻烦的地方,就是发布的时候,浏览器缓存可能读取的是旧版本的,这时候传统方法是使用query,可是query也有不少问题,文件发布顺序啥的会影响,并且须要手工加入版本号。 这里推荐webpack build 加hash。 咱们目前是使用的webpack 插件 webpack-html-plugin

三.Revert Proxy Cache

这里说的是反向代理(我以为叫服务端代理可能更容易理解)反向代理有保护服务器安全(将外部请求转发给内部服务器,同时将内部服务器响应转发给Client),缓存加速和实现负载均衡的做用。如今咱们的站点测试环境使用的是Nginx1.8.x 。而生产环境使用的是淘宝Tengineer

四.web Server Cache

咱们系统使用的tomcat服务器,因此在这里讲一下tomcat服务器的缓存策略。

tomcat默认是只对静态组员进行缓存对jsp是不缓存的。当前端发送一个请求时,服务端会根据这个资源的特征进行不一样的缓存策略。 本质上是存到一个map中,将etag或者last-modified做为value(前面讲浏览器缓存有说过),将资源的url做为一个key存储,当资源发生变化的时候咱们去更新这个map的value,当下次资源被请求会比较etag或者last-modified是否是最新的数据,若是是返回304.不是的话返回200和对应内容

tomcat源码中的!checkIfHeaders(request, response, cacheEntry.attributes)) 是整个机制的核心,用来判断资源是否须要从新获取,返回false就从新获取

 

protected boolean checkIfHeaders(HttpServletRequest request,
                                     HttpServletResponse response,
                                     ResourceAttributes resourceAttributes)
        throws IOException {

        return checkIfMatch(request, response, resourceAttributes)
            && checkIfModifiedSince(request, response, resourceAttributes)
            && checkIfNoneMatch(request, response, resourceAttributes)
            && checkIfUnmodifiedSince(request, response, resourceAttributes);

    }

这里四个check都返回true(都认为资源被改变了),那么checkIfHeaders久返回true,对应就要从新获取而不走缓存

 

五.membercache,redis  etc

 

在这里使用memcached大大缓解了数据库的读压力,固然对于写仍是会形成负担的(更新数据, 须要同时更新数据库和缓存),好在大部分应用都是读远大于写,使用simple spring memcached大大简化了缓存配置,ssm在key生成规则和list缓存上作的很好。有时间我也会进一步了解下。

 

六.code cache

 

环境:reflux + react

使用react-router-loader实现按需打包加载运用在SPA必定程度解决了单页应用程序性能问题。使用后基本上每一个页面初次访问都在200kb~300kb,并且静态资源能够获得缓存。但如今存在一个问题就是用户在多页面切换的时候回发送不少请求。以个人页面为例,每次要发送6个请求。可能就有600-700ms延迟。下图是切换三次页面的网络请求结果

在这里我打算作一次代码缓存,也就说只有第一次加载我从服务器拿,其余时候我是从code cache中拿。 只有用户强制刷新页面才去后台拿数据。

对于get操做

首先将cache挂载到store上,这里利用了Store单例的特性,即应用只有一份数据,这样不会出现脏数据的状况,而后get以前判断缓存中有没有,若是有直接返回,不然发送一个网络请求

若是请求成功,而且缓存不存在就添加缓存(第一次请求缓存是不存在的)。若是存在缓存不作处理。

对于update操做

直接在请求成功后的回调方法中更新对应缓存。

能够看下效果

博客不能够上传视频,悲哀啊。那就图片吧。

看下优化后的结果:

只有第一次请求会加载6次之后都是不会请求的

 

参考资料

[浏览器缓存机制剖析](https://juejin.im/post/58eacff90ce4630058668257)

相关文章
相关标签/搜索