浏览器缓存和Nginx缓存
缓存是提升用户访问速度,节省带宽,减轻服务器压力的必经之道。
下面都是针对的Http 1.1来说明,HTTP缓存都是针对浏览器客户端,其他第三方客户端不考虑。
使用缓存是最有效的提高访问速度的方法,在web服务器的场景当中不仅要考虑nginx作为缓存服务时候的使用方法还得考虑浏览器中的缓存失效的场景,因为浏览器缓存是否生效可以通过nginx指令去控制。浏览器的缓存对用户体验也是最大的。
使用浏览器缓存,即本地缓存,只需要读取本地的磁盘或者内存就可以了。通常是同时使用浏览器和nginx缓存。
基本原理和概念
相同的两次请求返回的结果相同时,第一次返回的结果缓存在客户端,第二次服务端不再返回结果,仅返回一个特殊的状态码,告诉客户端第二次请求的结果与上次相同,可以直接使用上次返回的数据。
实现中,会用到HTTP头中的两个字段:
-
ETag 返回应答数据的标记,服务端生成发送给客户端
-
If-None-Match 同样的请求,上一次返回的 ETag 值
交互过程
-
服务端在将数据发送给客户端之前,首先计算应答数据的摘要(通常是MD5),把计算结果作为 ETag 的值,和数据一同发送给客户端。
-
客户端收到应答数据后,检测 HTTP Header 中是否有 ETag 字段,如有则缓存应答数据和 ETag 的值。
-
客户端再次发起同一请求时,读取上次缓存的 ETag 值,将其作为 If-None-Match 的值,并与请求数据一同发送给服务端。
-
服务端收到请求,执行请求,在把应答数据返回给客户端之前计算摘要,并与客户端上报的摘要比较,如果两次摘要相同,说明本次的应答数据与上一次请求的应答数据相同,且客户端已缓存该数据,则简单返回304。
-
客户端收到304,直接读取本地缓存的数据返回给调用网络模块的业务方。
交互过程总结如下图:
如果你禁用浏览器缓存 ,打开一个新的网页每次你刷新网页都会返回200而不是304。
缓存模式
浏览器缓存可以分为两种模式,强缓存
和协商缓存
。
-
强缓存(无HTTP请求,无需协商)
直接读取本地缓存,无需跟服务端发送请求确认,http返回状态码是200(from memory cache或者from disk cache ,不同浏览器返回的信息不一致的)。
对应的
Http header
有:- Cache-Control
- Expires
-
协商缓存(有HTTP请求,需协商)
浏览器虽然发现了本地有该资源的缓存,但是不确定是否是最新的,于是想服务器询问,若服务器认为浏览器的缓存版本还可用,那么便会返回304(Not Modified) http状态码。
对应的
Http header
有:- Last-Modified
- ETag
Etag头部
Etag是存在于HTTP响应当中的,是Nginx向浏览器返回时添加的,它来标识一个特定版本的资源,Etag的值是通过上次修改时间的值以及资源的字节长度来生成的。
Syntax: etag on | off;
Default: etag on;
Context: http, server, location
生成规则:
ngx_sprintf(etag->value.data, "\"%xT-%xO\"",
r->headers_out.last_modified_time,
r->headers_out.content_length_n)
之前用户请求资源nginx返回了Etag给浏览器,浏览器也会缓存该Etag的值。当浏览器通过时间维度认为Etag值过期了,那么这个时候浏览器又想去nginx上重新读资源,比如nginx服务器上这个资源到底有没有修改过,如果没有修改过返回304给客户端,而不需要将完整的内容给客户端,比如这个文件有几十兆。
怎么样让nginx快速的判断这个资源有没有修改过。浏览器会通过if-none-match头部在它的值里面填的是Etag的值,这样nginx收到if-none-match的时候就会做比较,当Etag标识出来的值和nginx的Etag值比较匹配的时候才会返回200,否则验证失败返回304表示没又改变。
If-None-Match
举个例子
关闭缓存可以看到实际的请求发到上游服务器了,下面开始打开缓存
浏览器虽然发现了本地有该资源的缓存,但是不确定是否是最新的,于是想服务器询问,若服务器认为浏览器的缓存版本还可用,那么便会返回304(Not Modified) http状态码。
直接读取本地缓存,无需跟服务端发送请求确认,http返回状态码是200(from memory cache或者from disk cache ,不同浏览器返回的信息不一致的)。