简介

为了提高网站的访问速度和效率,我们需要设计各种各样的缓存,通过缓存可以避免不必要的额外数据传输和请求,从而提升网站的请求速度。对于HTTP协议来说,本身就自带有HTTP缓存。

今天我们就深入探讨一下HTTP中的缓存机制和使用。

HTTP中的缓存种类

缓存就是将请求的资源在本地保存一份拷贝,从而在下一次请求的时候,直接返回该拷贝,不用再从服务器下载资源,从而减少了资源的传输提升了效率。

除了直接访问和返回资源之外,HTTP中的缓存可以分成两类,一种是共享cache,也就是说不同的客户端都可以从该共享cache中获取资源,并且这些资源是多个客户端可以访问的。还有一种是私有cache,这意味着该cache只能用户或者客户端私有访问,其他用户是无权访问的。

私有cache很好理解,我们常用的浏览器中的cache基本上就是私有cache,这些cache是浏览器独有的,并不会共享给其他的浏览器。

共享cache主要用在一些web代理上,比如web代理服务器,因为web代理服务器可能会为众多的用户提供资源服务,对于这些用户共同访问的资源就不必要每个用户保存一份了,只需要在web代理服务器中保存一份即可,这样可以减少资源的无效拷贝。

HTTP中缓存响应的状态

对于HTTP缓存来说,一般缓存的是GET请求,因为GET请求除了URI之外,并没有其他多余的参数,并且其表示的意义是从服务器获取资源。

不同的GET请求,会返回不同的状态码。

如果是成功返回资源,则会返回200表示OK。

如果是重定向,则返回301。如果是异常,则返回404。如果是不完全的结果,则会返回206。

HTTP中的缓存控制

HTTP中的缓存控制是通过HTTP头来表示的。在HTTP1.1中加入了Cache-Control,我们可以通过Cache-Control来控制请求和响应的缓存情况。

如果不需要缓存,则使用:

Cache-Control: no-store

如果需要对客户端的缓存进行验证,则使用:

Cache-Control: no-cache

如果要强制进行验证,则可以使用:

Cache-Control: must-revalidate

在这种情况下,过期的资源将不会被允许使用。

对于服务器来说,可以通过Cache-Control来控制缓存是private或者public的:

Cache-Control: private
Cache-Control: public

还有一个非常重要的缓存控制就是过期时间:

Cache-Control: max-age=31536000

通过设置max-age,可以覆盖Expires头,表示在这个时间区间范围之类,该资源可以看做是最新的,不需要重新从服务器获取。

Cache-Control是HTTP1.1中定义的header字段,在HTTP1.0中也有一个类似的字段叫做Pragma。通过设置 Pragma: no-cache可以得到类似Cache-Control: no-cache的效果。也就是强制客户端重新提交缓存到服务器端进行校验。

但是对于服务器端的响应来说,并不包含Pragma,所以Pragma并不能完全替代Cache-Control。

缓存刷新

缓存存放在客户端之后,就可以在请求的时候被使用了。但是为了安全起见,我们需要给缓存设置一个过期时间,只有在过期时间之前的时间范围,缓存才是有效的,如果超过了过期时间,则需要从服务器重新获取。

这样的机制能够保证客户端获取到的资源始终是最新的。并且能够保证服务器端对资源的更新能够及时到达客户端。

如果客户端的资源在过期时间之类,那么这个资源的状态就是fresh,否则资源的状态就是stale。

如果资源是stale状态的,该资源并不会立即从客户端清理出去,而是在下一次的请求中,向服务器发送一个If-None-Match的请求,判断该资源在服务器端是否仍然是fresh状态的,如果该资源并没有发生变化,则返回304 (Not Modified),表示该资源仍然有效。

而这个fresh的持续时间就是通过"Cache-Control: max-age=N" 来判断的。

如果响应中并没有这个头,则会去判断 Expires header 是否存在,如果存在那么fresh的时间就可以使用Expires - Date 来进行计算。

如果响应中连Expires header都没有,那么怎么去判断资源的fresh时间呢?

这种情况下会去查找Last-Modified header,如果这个header存在的话,那么fresh时间就是(Date - Last-modified )/ 10 。

revving

为了提升HTTP请求的效率,我们当然希望缓存时间越长越好,但是前面我们也提到了,缓存时间过长会导致服务器资源更新困难的问题。怎么解决呢?

对于那些不经常更新的文件,请求他们的URL可以由文件名+版本号来决定。同一个版本号表示该资源内容是固定不变的,我们可以对其缓存一个非常长的时间。

当服务器资源内容发生变化之后,只需要在请求的时候更新版本号即可。

虽然这样的操作会造成服务器资源的修改同时要修改客户端请求的版本,但是在现代前端打包工具的帮助下,这并不是一个很大的问题。

缓存校验

当缓存的资源过期之后,有两种处理方式,一种是重新从服务器请求资源,一种是对缓存资源进行再次校验。

当然再次校验需要服务器的支持,并需要设置"Cache-Control: must-revalidate"请求头。

那么客户端怎么去校验资源是否有效呢?很明显我们不能把资源从客户端发送到服务器端进行校验,这样的操作方式太过复杂,并且在文件比较大的请求下,会造成资源的浪费。

我们很容易想到的一种方法是对资源文件进行hash运行,只要发送这个hash运算的结果进行对比即可。

当然,在HTTP中,提供了一个ETags header,这header可以看做是资源的唯一标记,用来在客户端和服务器端进行校验。这样客户端就可以请求一个If-None-Match,让服务器判断该资源是否match。这种判断被称为强校验。

还有一种弱校验的方式,如果响应中带有Last-Modified,则客户端可以请求一个If-Modified-Since,来向服务器询问该文件是否发生了变化。

对于服务器端来说,它可以选择是否进行文件的校验,如果不进行校验,则可以直接返回一个200 OK状态码,并直接返回资源。如果进行校验,则返回一个304 Not Modified,表示客户端可以继续使用缓存的资源,同时还可返回一些其他的header字段,比如更新缓存的过期时间等。

Vary响应

在服务器响应的时候,可以带上Vary header。这个Vary header的值是响应头中的某个key,比如Content-Encoding,表示对某个encoding的资源进行缓存。

比如客户端首先请求:

GET /resource HTTP/1.1
Accept-Encoding: *

服务器端返回:

HTTP/1.1 200 OK
Content-Encoding: gzip
Vary: Content-Encoding

则将会把资源和gzip类型的Content-Encoding一起缓存起来。

当客户再次请求:

GET /resource HTTP/1.1
Accept-Encoding: br

因为当前缓存的资源encoding方式是gzip,和客户端接受的encoding方式并不一样,所以重新需要从服务器获取:

HTTP/1.1 200 OK
Content-Encoding: br
Vary: Content-Encoding

这时候,客户端又缓存了一个br格式的资源。

下次客户端再次请求br类型的资源,就可以命中缓存了。

总结一下,Vary的意思是将资源再通过其他的类型比如encoding进行区分和缓存。

但是这样也会造成资源重复存储的问题,同一个资源因为编码格式的不同被缓存了很多份。为了解决这个问题,就需要对资源请求进行标准化。

所谓标准化,就是在请求之前对请求的encoding方式进行校验,只选择其中的一种编码方式进行请求,从而避免资源多次缓存的情况。

总结

到此,HTTP缓存就介绍完毕了,大家可以在实际的应用中对HTTP缓存加深理解。

本文已收录于 http://www.flydean.com/04-http-cache/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

HTTP系列之:HTTP缓存的更多相关文章

  1. 拥抱.NET Core系列:MemoryCache 缓存选项

    在上一篇 "拥抱.NET Core系列:MemoryCache 缓存过期" 中我们详细的了解了缓存过期相关的内容,今天我们来介绍一下 MSCache 中的 Options,由此来介 ...

  2. 拥抱.NET Core系列:MemoryCache 缓存域

    在上一篇“<拥抱.NET Core系列:MemoryCache 缓存选项>”我们介绍了一些 MSCache 的机制,今天我们来介绍一下 MSCache 中的缓存域. MSCache项目 M ...

  3. 【安卓中的缓存策略系列】安卓缓存策略之综合应用ImageLoader实现照片墙的效果

    在前面的[安卓缓存策略系列]安卓缓存之内存缓存LruCache和[安卓缓存策略系列]安卓缓存策略之磁盘缓存DiskLruCache这两篇博客中已经将安卓中的缓存策略的理论知识进行过详细讲解,还没看过这 ...

  4. 【安卓中的缓存策略系列】安卓缓存策略之磁盘缓存DiskLruCache

    安卓中的缓存包括两种情况即内存缓存与磁盘缓存,其中内存缓存主要是使用LruCache这个类,其中内存缓存我在[安卓中的缓存策略系列]安卓缓存策略之内存缓存LruCache中已经进行过详细讲解,如看官还 ...

  5. 拥抱.NET Core系列:MemoryCache 缓存域(转载)

    阅读目录 MSCache项目 缓存域 写在最后 在上一篇“<拥抱.NET Core系列:MemoryCache 缓存选项>”我们介绍了一些 MSCache 的机制,今天我们来介绍一下 MS ...

  6. 拥抱.NET Core系列:MemoryCache 缓存选项 (转载)

    阅读目录 MSCache项目 MemoryCacheOptions ExpirationScanFrequency SizeLimit CompactionPercentage 写在最后 在上一篇 ” ...

  7. Android批量图片加载经典系列——采用二级缓存、异步加载网络图片

    一.问题描述 Android应用中经常涉及从网络中加载大量图片,为提升加载速度和效率,减少网络流量都会采用二级缓存和异步加载机制,所谓二级缓存就是通过先从内存中获取.再从文件中获取,最后才会访问网络. ...

  8. Android批量图片加载经典系列——使用二级缓存、异步网络负载形象

    一.问题描写叙述 Android应用中常常涉及从网络中载入大量图片,为提升载入速度和效率,降低网络流量都会採用二级缓存和异步载入机制.所谓二级缓存就是通过先从内存中获取.再从文件里获取,最后才会訪问网 ...

  9. 拥抱.NET Core系列:MemoryCache 缓存过期

    在上一篇"拥抱.NET Core系列:MemoryCache 初识"中我们基本了解了缓存的添加.删除.获取,那么今天我们来看看缓存的过期机制.这里和上篇一样将把"Micr ...

  10. 拥抱.NET Core系列:MemoryCache 缓存过期 (转载)

    阅读目录 MSCache项目 MSCache提供的过期方式 绝对时间到期 滑动时间到期 自定义过期策略 过期策略组合拳 缓存过期回调 写在最后 在上一篇”拥抱.NET Core系列:MemoryCac ...

随机推荐

  1. POJ1704 Georgia and Bob 题解

    阶梯博弈的变形.不知道的话还是一道挺神的题. 将所有的棋子两两绑在一起,对于奇数个棋子的情况,将其与起点看作一组.于是便可以将一组棋子的中间格子数看作一推石子.对靠右棋子的操作是取石子,而对左棋子的操 ...

  2. 前端基础html(二)

    一.html的概念 1.概念:超文本标记语言. 2.超文本,超链接:超级不仅有文本,图片,还有音频,视频等. 3.html:作用:   显示服务器端的响应结果. 二.互联网三大基石 1.url:统一资 ...

  3. C++第四十三篇 -- VS2017创建控制台程序勾选MFC类库

    用VS2017创建EXE带MFC类库方法 1. File --> New --> Project 2. Windows桌面向导 3. 勾选MFC类库 4. 创建成功 如果项目编译出错 1. ...

  4. 旧VC项目dpiAware支持

    起因 工作原因,需要维护一款VS2008 SP1开发的MFC项目, 发现WIN10高分辨率下显示模糊,不考虑升级VC版本情况下尝试解决 尝试 新版本VC中Manifest Tool>Input ...

  5. Python实现猜数字游戏

    Python中实现猜数字游戏代码如下: import random # 引入随机数标准库-random # 定义数字上下限和最大游戏次数 min_num = 1 max_num = 10 guess_ ...

  6. 在vmware虚拟机下的Window2003服务器下安装IIS服务详细教程——超级详细(解决关于:800a0bb9的解决办法)

    总的来说,就是9步: 1.控制面板添加或者删除程序2.删除想要删的3.打开IIS配置4.开始共享5.导入源码6.配置权限7.网站属性.文档.应用程序配置8.web服务扩展9.访问网站 在安装好虚拟机的 ...

  7. 腾讯云TDSQL监控库密码忘记问题解决实战

    首先,给大家介绍一下TDSQL.TDSQL MySQL 版(TDSQL for MySQL)是腾讯打造的一款分布式数据库产品,具备强一致高可用.全球部署架构.分布式水平扩展.高性能.企业级安全等特性, ...

  8. 三、Linux部署MinIO分布式集群

    MinIO的官方网站非常详细,以下只是本人学习过程的整理 一.MinIO的基本概念 二.Windows安装与简单使用MinIO 三.Linux部署MinIO分布式集群 四.C#简单操作MinIO 一. ...

  9. rein 多平台支持的超便携端口转发与内网穿透工具

    介绍 本程序主要用于进行反向代理IP地址和端口,功能类似于 nginx 的 stream 模式和rinetd 的功能:在(1.0.5)版本开始,rein支持内网穿透,这一功能类似于frp 和ngrok ...

  10. 浅析Java断言

    Java断言 1.断言的概念 Java的断言机制assert是一种用于测试阶段的语法特性,它允许我们在测试期间向代码中插入一些检查语句.代码发布时这些检测语句将被自动移除. 断言关键字assert有下 ...