缓存那些事

前端之家收集整理的这篇文章主要介绍了缓存那些事前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

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

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

一.Browser Cache

http协议有自己的缓存协商机制。

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

http1.1使用三种

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

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

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,resourceAttributes)
&& checkIfModifiedSince(request,resourceAttributes)
&& checkIfNoneMatch(request,resourceAttributes)
&& checkIfUnmodifiedSince(request,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)

猜你在找的React相关文章