如何在SpringBoot中设置HTTP缓存,你知道么?

点击上方“Java基基”,选择“设为星标”

做积极的人,而不是积极废人!

每天 14:00 更新文章,每天掉亿点点头发...

源码精品专栏

 

来源:blog.csdn.net/w57685321/

article/details/92797551


6432a02a170da3b0076f2bdf5cd7294e.png

介绍

工作之余读取缓存相关的功课时,看到了http缓存相关的知识,HTTP缓存机制是一个网络优化的重要手段,不管是前端还是做网络后台,都可能会用到它,应该是体系中的一个基础资料,很早就是这一块学的资料,现在整理整理到现在。

HTTP可以说是浏览器缓存的一种,浏览器缓存也包含很多内容:HTTP缓存、indexDB、缓存、本地存储等等。这里我们只讨论HTTP缓存相关内容。

浏览器主要分为Last-Modified/EtagCache-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/

缓存控制

07559d4f378197ef3221e1e912c15c0b.png

cache-control这里的地方,像这样设置cache-control是没有缓存的

在请求中使用Cache-Control的时候,它的任选价值有:

d607a77bbd0b3523118d94037682baed.png

在响应中使用Cache-Control的时候,它任选的值有:

940f9f9bc9cb55f7eca9f36b4afde705.png

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,所以有可能不会,所以一般是为了手机会都写上。

fe9ba75dcbc115f6cb12f7d53e16d71c.png

Cache-Control设置时间长度、Expires 设置时间点

Last-Modify/If-Modify-Since

浏览器在第一次资源的时候,服务器返回的标题中会加上请求Last-ModifyLast-modify是一个时间标识资源的最后修改时间。

9cb15cfe85bdfcc6a814a618947c1e0d.png

当浏览器再次请求该资源时,会在请求头中包含该资源时,应在收到请求If-Modify-Since之前及时返回Last-Modify。根据服务器If-Modify-Since的最后时间判断,是否发送的资源缓存。

4acd74c0b1f8b5b08445cc85f08d9040.png

如果缓存,则返回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值来判断是否命中缓存。

32d830d360fdaab2d65a0ac2ae2c13f2.png

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。

546de9e5ea80576e772006fd6bba859e.png 698c28518410ab62b740bd505b5345ea.png

浏览器优先级和优先级:ETag优先级>Last-Mo级、缓存控制级>过期。

用户操作与缓存

e1f324cf59435af6235ae224d51e77b1.png

在spring boot中开启httpCache

本地测试环境:

  • Spring Boot 版本:2.1.3.RELEASE

  • 项目前一起通过ajax,寻找文件全部搜索src//静态目录下,跟后台部署。

实操:

配置Cache-Control

spring boot 中,没有响应从缓存相关的配置默认是这样子的发现,响应没有使用缓存的 http 端,导致每次在服务获取数据时,总有些资源文件加载是每个组件次从服务器去获取的,像常用的js库、css库什么的。

2bd3cf0d5694e6187c9cd2413d12c955.png

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官方文档最后的附件里面有。

7be8ac7e8485ddf93f099aaf34aec4ff.png

再来测试这个,先把缓存开启,它的选项就是通过request headers的CacheControl实现的。

6ec417dd652def38c60ecc4ee4cda6e2.png

然后发现已经使用了httpcache。

f425c9ab49d8fe5fd08757b41c2d4095.png

发现之前已经默认的无存储了,反而变成了我们自己设置的过渡时间了,说明已经在http的强缓存了!

小提示:这个addResourceHandleraddResourceLocations不要弄反了,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图像映射到/的网址下

72ab65a60f91a6d06aa57c2af3f6774b.png

如果出现问题,那么就调试 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

18a986c2d0950f6fcb32cc9ad84305b3.png

发现已经变成了304,设置成功了。

6d391775c939ed2a9241204406146b88.png

现在就是通过对比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的规则

05b044da7c3eaac1f2ccf4206add7a72.png

第一次请求,服务器会返回一个Etag标签,200个状态码

0ee41874b46b593a0652f7a1cf2bb6a9.png

第二次请求,浏览器携带这个etag带去后台验证,然后返回304

f91e3200c0c94ea965eb27d932185a7d.png

修改这个文件,发现返回了一个新的Etag,再次请求也是304,说明缓存成功了。



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

67257e9b53ad7ff68d32d451c323779c.png

已在知识星球更新源码解析如下:

6a58f0b30df0bc9e2c92371da931d8f5.jpeg

ec4c93c07032c8f8dd15dc8a72594a53.jpeg

caa5b4adf005144fec652103daf27723.jpeg

f53910d60165880ef0567fe1e84313d9.jpeg

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 6W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值