点击上方“Java基基”,选择“设为星标”
做积极的人,而不是积极废人!
每天 14:00 更新文章,每天掉亿点点头发...
源码精品专栏
介绍
工作之余读取缓存相关的功课时,看到了http缓存相关的知识,HTTP缓存机制是一个网络优化的重要手段,不管是前端还是做网络后台,都可能会用到它,应该是体系中的一个基础资料,很早就是这一块学的资料,现在整理整理到现在。
HTTP可以说是浏览器缓存的一种,浏览器缓存也包含很多内容:HTTP缓存、indexDB、缓存、本地存储等等。这里我们只讨论HTTP缓存相关内容。
浏览器主要分为Last-Modified/Etag
和Cache-Control/Expires
其中Cache-Control/Expires
属于高级缓存,Last-Modified/Etag
适合(比较)缓存
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
视频教程:https://doc.iocoder.cn/video/
缓存控制
cache-control
这里的地方,像这样设置cache-control
是没有缓存的
在请求中使用Cache-Control
的时候,它的任选价值有:
在响应中使用Cache-Control的时候,它任选的值有:
在Cache-Control
中,这些值可以自由组合,多个值如果突破时,也是有优先级的,而no-store
优先级最高。
像cache-control
这种强缓存是性能高的,它不需要请求服务器,就直接从本地加载文件了,后面Last-Modified/Etag
需要请求服务器来验证文件的。
所以浏览器会先验证高速缓存 ,所以对于g、cs的轮播时间设置非常好,因为浏览器打包的文件可以是实际使用中的有效工具,比如可以配置webpack后的文件js文件跟个hash值,就可以根据hash来判断文件内容是否发生变化。在打包出来的文件名上加上hash是目前最常用的有效使用器强缓存的方法,js文件是否有内容更新,hash就更新,浏览器请求路径变化缓存,如果js内容不变,hash不变,直接用缓存,下面的etag也有类似的想法。
如果今天这个文件,所以比较的时间文件不要加cache-control
了 缓存配置缓存控制的过渡期的话,cache-control
因为优先级Last-Modified/Etag
比高,所以在这段时间内就不会更新了。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://gitee.com/zhijiantianya/yudao-cloud
视频教程:https://doc.iocoder.cn/video/
过期
关于过期:
Expires 是以前使用控制缓存的http头,Cache-Control 是新版的API。
现在是 Cache-Control。
如果在Cache-Control响应头设置了“max-age”或者“s-max-age”指令,那么Expires头会被加载。
响应头设置方式:Expires: Wed, 21 Oct 2015 07:28:00 GMT
Expires 响应头包含日期/时间,即在此之后的时间,响应过渡。
注意:因为时间标准的使用是本地的,所以时间不可靠,所以要使用而
Cache-Control
不是过期。
关于第三点:一般只有上传Cache-Control
和Expires都设置上,因为Expires是HTTP 1.0定义的字段,Cache-Control
只是HTTP 1.1的支持字段,万一客户端Cache-Control
工作,HTTP 1.0,所以有可能不会,所以一般是为了手机会都写上。
Cache-Control
设置时间长度、Expires 设置时间点
Last-Modify/If-Modify-Since
浏览器在第一次资源的时候,服务器返回的标题中会加上请求Last-Modify
,Last-modify
是一个时间标识资源的最后修改时间。
当浏览器再次请求该资源时,会在请求头中包含该资源时,应在收到请求If-Modify-Since
之前及时返回Last-Modify
。根据服务器If-Modify-Since
的最后时间判断,是否发送的资源缓存。
如果缓存,则返回http304,并且不会返回资源,则修改时间不会返回Last-Modify
。由于服务器的时间,所以与服务端不会导致客户端问题。但是端通过脚本最终时间来来资源是否修改还是正确的(资源变化了最后时间修改也可以一致)。于是出现了ETag/If-None-Match
。
Etag/If-None-Match
在上面的缓存之后,若未命中强,则服务器浏览器发送码为http发送。服务器根据中头的信息Last-Modify/If-Modify-Since
或来判断是否命中命中。Etag/If-None-Match
缓存中加载资源。
Last-Modify/If-Modify-Since
不同的是,返回一个状态码与(ETag:entity tag) 。ETagEtag/If-None-Match
可以保证每个资源是唯一的,变化导致ETag变化。ETag值的变更则说明资源已经被修改。服务器根据修改上发送的If-None-Match
值来判断是否命中缓存。
ETag是HTTP1.1的一个属性,用来帮助服务器控制Web端中的缓存。它的原理是这样的,当请求服务器的某个项资源(A)时,服务器根据A算出一个哈希浏览器值( 3f80f-1b6-3e1cb03b
)并通过ETag返回给服务器,浏览器“ 3f80f-1b6-3e1cb03b
”和A缓存在本地,等下次再次向服务器请求时,会通过类似If-None-Match
:“ 3f80f-1b6-3e1cb03b
”的头把ETag发送给服务器,服务器再次计算作为一个哈希值并和浏览器返回的值比较做,如果发现A发生变化就返回给浏览器(200),如果发现A没有变化就给浏览器返回一个304未。这样通过浏览器端缓存,可以提供服务器的全部量,因为服务器每次都返回数据给
HTTP中并没有指定如何生成Etag,可以自己定义一种好的方式来生成Etag
通过etag,其实还是要向服务器发送一次请求,但是发送资源文件缓存了,减少了服务器的重心。
总结
Last-Modified 与 Etag对比
你可能会觉得使用 Last-Modified 已经让浏览器知道本地的原因的副本是否是新的,那么还需要 Etag(实体)HTTP1.1 中 Etag 的主要是为了解决几个 Last-Modified 比较难出现解决的问题:
上次修改后标注的修改最多可以精确到几秒钟,如果文件在 1 秒以内,被修改多次的话,不能标注文件的时间。
如果某些文件内容会被定期生成,有时并没有任何变化,但最后修改了,导致文件无法使用缓存。
有可能服务器没有正确获取,或者与代理修改文件存在时间等情况。
E 是服务器端的自动生成的资源,唯一的生成是由开发者生成的资源在服务器端的唯一生成,并能够始终准确地控制缓存。ETagLast-Modified
可以一起,服务器会优先标记,或者一致的情况下,将继续使用比对Last-Modified
,最后才决定是否返回304。
浏览器优先级和优先级:ETag优先级>Last-Mo级、缓存控制级>过期。
用户操作与缓存
在spring boot中开启httpCache
本地测试环境:
Spring Boot 版本:2.1.3.RELEASE
项目前一起通过ajax,寻找文件全部搜索src//静态目录下,跟后台部署。
实操:
配置Cache-Control
spring boot 中,没有响应从缓存相关的配置默认是这样子的发现,响应没有使用缓存的 http 端,导致每次在服务获取数据时,总有些资源文件加载是每个组件次从服务器去获取的,像常用的js库、css库什么的。
spring boot配置cache-control
和expires可以通过WebMvcConfigurationSupport
来配置,也可以通过修改配置文件的方式,这里使用配置类的方式。
这里需要注意的是,使用可能会导致使用的自动配置不生效,因为其中的配置,可能默认的一些配置方式无法实现,如果想保留的配置,WebMvcConfigurers 的方式来WebMvcConfigurationSupport
实现WebMvcAutoConfiguration``WebMvcAutoConfiguration``@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@Configuration
public class WebConfigConfigurer extends WebMvcConfigurationSupport {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(604800, TimeUnit.SECONDS));
}
}
这里将src/main/resources/static
目录映射到/static
的url下面,然后setCacheControl就可以了。
spring.resources.cache.cachecontrol.cache-private= # Indicate that the response message is intended for a single user and must not be stored by a shared cache.
spring.resources.cache.cachecontrol.cache-public= # Indicate that any cache may store the response.
spring.resources.cache.cachecontrol.max-age= # Maximum time the response should be cached, in seconds if no duration suffix is not specified.
spring.resources.cache.cachecontrol.must-revalidate= # Indicate that once it has become stale, a cache must not use the response without re-validating it with the server.
spring.resources.cache.cachecontrol.no-cache= # Indicate that the cached response can be reused only if re-validated with the server.
spring.resources.cache.cachecontrol.no-store= # Indicate to not cache the response in any case.
spring.resources.cache.cachecontrol.no-transform= # Indicate intermediaries (caches and others) that they should not transform the response content.
spring.resources.cache.cachecontrol.proxy-revalidate= # Same meaning as the "must-revalidate" directive, except that it does not apply to private caches.
spring.resources.cache.cachecontrol.s-max-age= # Maximum time the response should be cached by shared caches, in seconds if no duration suffix is not specified.
spring.resources.cache.cachecontrol.stale-if-error= # Maximum time the response may be used when errors are encountered, in seconds if no duration suffix is not specified.
spring.resources.cache.cachecontrol.stale-while-revalidate= # Maximum time the response can be served after it becomes stale, in seconds if no duration suffix is not specified.
如果要使用配置的方式也很简单,这些都是关于缓存的配置,在spring boot官方文档最后的附件里面有。
再来测试这个,先把缓存开启,它的选项就是通过request headers的CacheControl实现的。
然后发现已经使用了httpcache。
发现之前已经默认的无存储了,反而变成了我们自己设置的过渡时间了,说明已经在http的强缓存了!
小提示:这个addResourceHandler
和addResourceLocations
不要弄反了,addResourceHandler
参数是 URL 路径模式,addResourceLocations
参数是本地资源路径
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(0, TimeUnit.SECONDS)
.cachePublic());
}
这种的,就是将src/main/resources/static
图像映射到/的网址下
如果出现问题,那么就调试 ResourceHandlerRegistry 的抽象处理程序映射 getHandlerMapping() 遇到方法,在 urlMap 变量中看到 url 与处理程序的映射关系
配置Last-ModifiedCache
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(
CacheControl.maxAge(0, TimeUnit.SECONDS)
.cachePublic());
}
如果遇到问题,那么就调试ResourceHandlerRegistry AbstractHandlerMapping getHandlerMapping()
,在urlMap变量中查看url与handler的映射关系
配置Last-ModifiedCache
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(
CacheControl.maxAge(0, TimeUnit.SECONDS)
.cachePublic());
}
一步一步的来,把cacheControl
过渡时间设置为0,并设置为之前的标签,因为间隔时间为0,判断已经过渡,根据判断,而这里没有设置标签,所以进入Last-Modified
判断。
需要注意的区别,no-cache是压根用缓存,直接服务器200就是时间请求的判断时间为0经常走no-cache
与缓存的区别max-age=0``Etag/Last-Modified
发现已经变成了304,设置成功了。
现在就是通过对比If-Modified-Since
时间Last-Modified
,就是修改文件来判断和缓存的。
配置ETagCache
@Bean
public FilterRegistrationBean filterRegistrationBean () {
ShallowEtagHeaderFilter eTagFilter = new ShallowEtagHeaderFilter();
//设置为weakETag,默认为false
// eTagFilter.setWriteWeakETag(true);
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(eTagFilter);
registration.addUrlPatterns("/static/*");
return registration;
}
如果想开启 Etag 可以使用这个过滤器,把静态下文件的设置上 etag
这个过滤器是通过生成MD5来校验的,也可以自定义一个生成Etag的规则
第一次请求,服务器会返回一个Etag标签,200个状态码
第二次请求,浏览器携带这个etag带去后台验证,然后返回304
修改这个文件,发现返回了一个新的Etag,再次请求也是304,说明缓存成功了。
欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢:
已在知识星球更新源码解析如下:
最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。
提供近 3W 行代码的 SpringBoot 示例,以及超 6W 行代码的电商微服务项目。
获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)