本章节来学习下Http缓存相关内容,这也是容易被忽略的一部分。我们利用Chrome和real_server_1项目进行测试。我们下面就来看下如何在Java应用层控制浏览器缓存。
HTTP缓存
Last-Modified
直接看real_server_1上实现的代码:
为了方便测试,文档的修改时间为每十秒钟更新一次。下面就来测试下:
首次访问
首次访问http://localhost:8081/last-modified
,将得到如下响应头:
响应状态吗200表示请求成功,另外,有如下几个缓存控制参数。
- Last-Modified:表示文档的最后修改时间,当去服务器验证时会用到这个时间
- Expires:http/1.0规范定义,表示文档在浏览器中的过期时间,当缓存内容时间超过这个时间,则需要重新去服务器获取最新的内容。
- Cache-Control:http/1.1规范定义,表示浏览器缓存控制,max-age=20表示文档可以在浏览器中缓存20秒
根据规范定义,Cache-Control优先级高于Expires。实际使用时可以两个都用,或仅使用Cache-Control就可以了。
F5刷新
接着按F5刷新当前页面(拒首次访问10秒内),将看到浏览器发送如下请求头:
此处发送时有一个If-Modified-Since请求头,其值是上次请求响应中的Last-Modified,即浏览器会用这个时间去服务端验证内容是否发生了变更,接着收到如下响应信息:
状态码为304,表示服务端通知浏览器”你缓存的内容没有变化,直接使用缓存内容展示吧”。
Ctrl+F5强制刷新
如果想强制从服务端获取最新的内容,可以按”Ctrl + F5”(“command + shift + R”)组合键。浏览器在请求时不会带上If-Modified-Since,但是会带上Cache-Control:no-cache和Pragma:no-cache,这是为了通知服务器提供一份最新的内容。
ETag
先直接看下etag实现:
其中Etag用于发送到服务器进行内容变更验证的,而Cache-Control用于控制缓存时间。此处使用了W\”343sda”,弱实体只要内容语义没变即可。Nginx在生成Etag时使用的算法是Last-Modified + Content-Length。经过测试,可以看出请求头带上了If-None-Match,其值为第一次访问http://localhost:8081/etag
时返回的Response Headers的Etag的值。
到目前为止我们一直没有看出Cache-Control的max-age或者Expires是如何起作用的。之前测试都是在浏览器地址栏刷新或者强制刷新当前请求,都会去服务端验证内容是否发生变更,跟max-age完全没关系。也就是说max-age起作用的点不在于当前请求,而是可以被别人缓存多久。典型的场景比如js、css、图片等外部资源的引用,或者页面跳转等。我们拿页面跳转来做个测试,修改下/etag请求的body部分:String body = "<a href = 'http://localhost:8081/last-modified'>点击跳转到/last-modified</a>";
,目的是为了看出max-age到底是对谁生效。理一下测试的场景,我们当前主页面为http://localhost:8081/etag
,其内容就是跳转到http://localhost:8081/last-modified
,当前页面的max-age为10秒,/last-modified页面的max-age为20秒。下面列出测试步骤
- 强制刷新
http://localhost:8081/etag
,然后立刻点击超链接,此时会跳转到http://localhost:8081/last-modified
,看下请求头:
- 点击浏览器后退按钮回到
http://localhost:8081/etag
,然后再点击超链接,请求到/last-modified的请求头:
可以看出Request Headers和之前大不相同,再结合服务端的debug来看,浏览器其实走的是本地cache,没有向服务端发出请求。当距离第一步20秒左右之后,再次执行本步奏,会发现点击超链接时,会发送/last-modified请求到服务端。综上看来这是max-age起的作用,其意思是/last-modified链接的内容可以被/etag缓存20秒。
Nginx HTTP缓存设置
Nginx托管的静态资源
Nginx 提供了expires、etag、if-modified-since指令来实现浏览器缓存控制。
我们下面来测试下:
访问http://localhost/img/1.jpg
,返回的Response Headers如下:
可见缓存所需要的请求都基本都有了,其中对于静态资源Nginx会自动添加Etag,可以通过etag off指令禁止生成Etag。
proxy_pass
在real_server_1项目中写一个新的请求,来看看nginx对proxy_pass的默认行为:
来看下访问http://localhost/cache_nginx
,返回的Response Headers如下:
可以看出expires 1d的设置是成功的,但是和Nginx对静态资源的处理有所不同。proxy_pass的Response Headers没有了ETag和Last-Modified,交由真正业务服务器自行处理。
Http缓存就学到这里,在实际使用中,index页面和其加载的外部资源该如何缓存,需要自行斟酌。同时别忘了还要结合是通过浏览器直接访问还是APP的webview访问,这两种方式对于index页面的访问行为有时候是不一样的,区别就在于max-age是否介入。