1.1 缓存解释

缓存通常是基于键值对来缓存的,键通过hash计算后,存放于内存某个空间,所以键可以理解为索引。而值是存放在内存空间或是磁盘空间上。

当用户的用户请求送达至Web服务器,Web服务器会对URL进行hash计算,然后比对缓存(hash表)中的键。如若命中,则根据与之对应的值找到数据存放的位置(这里的值可以理解为指针,指着对应数据存放的位置),从而获取到缓存的结果。


1.2 工作原理

1.2.1    缓存命中


① 客户端请求某个Web数据,会先送至缓存服务器中,缓存服务器本身会监听80号端口接收用户请求

② 当Web缓存服务器收到用户请求之后,会将这个请求送达至代理进程中

③ 进程拆除用户请求报文中的应用层首部,TCP首部,IP首部等,从而获取到请求报文中的URL

④ 对URL进行hash计算,然后和缓存服务器中hash表中的缓存键进行比对,若一致则缓存命中

⑤ 在对应的值所指向的内存或硬盘空间上找到对应的内容数据

⑥构建成响应报文,直接返回给客户端

1.2.2    缓存未命中


① 客户端请求某个Web数据,会先送至缓存服务器中,缓存服务器本身会监听80号端口接收用户请求

② 当Web缓存服务器收到用户请求之后,会将这个请求送达至代理进程中

③ 进程拆除用户请求报文中的应用层首部,TCP首部,IP首部等,从而获取到请求报文中的URL

④ 对URL进行hash计算,然后和缓存服务器中hash表中的缓存键进行比对,不一致则缓存未命中

⑤ 代理服务器会自行封装成请求报文,把自己当做http的客户端,向上游服务器发起请求

⑥ 若内容存在,上游服务器会构建成响应报文,返回给代理服务器。

⑦ 当代理服务器收到响应之后,会检查该对象是否可以缓存,如若可以,会对URL进行hash之后生成一个键,存放到对应的hash表中

⑧ 在相应的内存或磁盘空间上存储对应的内容数据

⑨ 当操作完成之后,会将数据构建成相应报文,然后响应给客户端

1.3 扩展

在用户自己内部的浏览器会有自己的缓存(私有缓存),当用户请求一个首页的时候,会向代理服务器请求,之后缓存在本地的一些链接或者图片,如果在本地存在缓存,那么就直接响应给用户,而无需再向代理服务器请求。所以我们的缓存是有存在多级的

二、缓存失效

现在存在一个问题,那就是如果我们的缓存失效了应该如何处理,正常而已如果缓存过期了,我们应该不再使用此缓存。所以缓存是具有TTL(生命时间值)值的,当这个TTL值过期之前,我们可以正常使用此缓存对象,一旦过期,请求一方就只能到比他更高一级的下一层缓存中的后端服务器去请求资源。

假设某个缓存对象可以缓存7天,但是在第2天,后端服务器已经修改了此对象,那么此时应该如何处理。所以用户如果直接从缓存中获取到的数据就已经不是最新的了。这样可能会导致缓存有效期过期之前用户将无法获取有效的结果。那么如果处理呢?

2.1 缓存的方式

基于绝对时间缓存
在http 1.0时使用的是绝对时间来缓存,可以理解精准的时间算法

基于条件式缓存

到 1.1版本以后,对其缓存功能进行了扩展,引入了几种功能

1、引入相对时长过期时间
2、引入强大可缓存对象控制功能(哪些数据能缓存以及不能缓存)
3、引入条件式缓存

2.2 基于绝对时间

比方说,此次缓存时为中国时区的2016年7月3日9:00 缓存的,缓存的TTL值为7天,那么过期时间应该就是 7天之后(2016年7月10日)9:00 过期,但是如果是在美国,或者欧洲,因为时区的不同,使用绝对时间将会导致缓存缓存不

2.3 基于条件式缓存

基于最后一次缓存时间后询问式验证请求

当用户访问同一个URL时,会发现在自己的缓存空间中存在一个相同的缓存对象,此时他不会了立即使用此缓存,而是去向上游服务器去验证这个缓存是否过期。主动向服务器发起验证请求,去请求一个URL,而且会发一个独特的请求首部(If-Modified-Sinces:time),询问在此时间之后(也就是缓存对象建立的时间之后),你是否发生了修改。如果后端服务器发现此资源未修改,会响应304(原始数据未修改)的响应码,由于资源未改变,所以此处发送的仅仅是响应码,数据无需发送。当客户端接收到响应后,就会直接使用本地的缓存。

如果后端服务器发现此资源已经修改,那么会找到对应的资源,然后发一个200(原始数据已修改)的响应码,并将资源发送给客户端。当客户端接收到后,会替换原先缓存下来的结果,并在浏览器上进行显示。
但是基于时间来记录判定其实并不理想,假设用户发验证时,而你的服务器时间并未到那个时间,那么就会无法验证缓存时间了。

基于标签(tag)进行条件式请求

在服务器端,每一个文件、或者是资源,每次版本修改之后都会附带一个tag(这个tag可能是一个随机生成的数,所以可以理解具有唯一性)。当用户请求资源后,会发送一个独特的请求首部(If-None-Match:tag),会将本地缓存的tag发送给服务器端,询问资源的tag是否匹配,匹配则资源未改变,响应304响应码,否则资源已改变,响应200响应码。

所以这样就不会担心基于时间记录时所产生的各种问题。

三、HTTP的缓存工作模式

3.1 基于缓存过期时间

1、当用户第一次访问资源时,缓存服务器不存在对应的缓存对象,那么此时缓存服务器会向后端服务器请求数据,后端服务器会数据响应给缓存服务器,并附带Cache-Control首部信息,表示缓存的时间。然后缓存服务器会将数据缓存于本地,然后将数据响应给客户端。

2、当客户端再次请求同一个资源时,如果缓存时间未到期,那么此时缓存服务器会直接将用户请求的资源直接响应给客户端。

3、如果当客户端请求资源时发现缓存服务器里的缓存周期已过期,那么此时缓存服务器会向后端请求资源,并将信息资源缓存于本地,而此时也更新了缓存的TTL值,然后响应给客户端。

3.2 基于条件式缓存

1、当客户端第一次请求资源时,由于缓存服务器并没有响应的缓存资源,那么此时缓存服务器会向后端服务器发起资源请求。当后端服务器收到请求后,会响应缓存服务器的请求,并附带Last-Modified:time的首部信息,缓存服务器会将资源及Last-Modified信息缓存于本地,然后一同也将这些信息响应给客户端。

2、客户端发起资源请求,如果缓存服务器有对应的资源缓存条目,但是此时客户端不会直接使用,而是发送了If-Modified-Since:time请求首部给后端服务器,来询问是否数据未修改。如数据未修改,会返回304的响应码,那么此时客户端会直接使用缓存服务器里的资源。

3、类似上面的方法,但是如果后端服务器响应的是200(数据已修改)的响应码时,那么缓存服务器会接受下这段响应信息,并将数据缓存于本地,然后再讲资源 + Last-Modified响应信息发回给客户端。

3.3 组合应用HTTP首部

根据上面的介绍,我们知道,如果基于时间来判断,可能会导致数据不准确的情况。而使用基于条件式的缓存可以弥补基于时间的不足,但是它自身还有一个缺点,那就是需要不断的去询问后端服务器的数据。这样就消耗占用了后端服务器的资源以及带宽。所以我们可以使用基于两种方式的合并来完成缓存失效判断。

1、第一次用户访问,那么类似前面介绍的方法,不同的是,此时缓存服务器会记录 Etag信息和 Cache-Control信息。

2、如果客户端请求的相同的资源,如果缓存未过期,那么此时使用的仍然是缓存服务器上的资源。

3、当缓存服务器的资源已失效,那么客户端会向后端服务器发起If-None-Match:Etag请求首部,向后端服务器确认资源是否已被修改,如果资源未修改,此时服务器会响应304(资源未修改)的响应码,那么缓存服务器会更新TTL值,并响应给客户端。