⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/w57685321/article/details/92797551 「0x2015」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

介绍

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

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

浏览器主要分为Last-Modified/EtagCache-Control/Expires

其中Cache-Control/Expires属于高级缓存,Last-Modified/Etag适合(比较)缓存

缓存控制

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比高,所以在这段时间内就不会更新了。

过期

关于过期:

  • 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-ModifyLast-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的强缓存了!

小提示:这个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图像映射到/的网址下

如果出现问题,那么就调试 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,说明缓存成功了。

文章目录
  1. 1. 介绍
  2. 2. 缓存控制
  3. 3. 过期
  4. 4. Last-Modify/If-Modify-Since
  5. 5. Etag/If-None-Match
  6. 6. 总结
  7. 7. 在spring boot中开启httpCache
    1. 7.0.1. 配置Cache-Control
    2. 7.0.2. 配置Last-ModifiedCache
    3. 7.0.3. 配置Last-ModifiedCache
    4. 7.0.4. 配置ETagCache