tomcat缓存静态资源深入
之前看过apach及nginx对于静态资源(含js,图片,css等)部分的缓存,用于加速并减轻后台实际web服务器的压力。
静态资源缓存是WEB服务器优化的一种手段,基本原理如下:
1.客户端浏览器请求服务器一个服务(该服务含有图片,js等静态资源),通常会对于每一个网页中的独立图片或js文件发送一个http请求
2.WEB服务器对于每个资源HTTP请求进行解析,并生成一个资源修改时间的唯一值(可以是etag或last_modified参数),放入服务器端map,key为资源url,value为资源修改时间。最后将此资源修改时间的唯一值包含在http头上返回,因为是首次请求,所以会将所有内容放在http body中一并返回给客户浏览器端
3.客户浏览器接收服服务器响应,并将服务器返回的资源修改时间作为key放入浏览器客户端,value为http body中的实际资源内容
4.客户浏览器再次请求静态资源时,会将资源修改时间一并发送给服务器
5.服务端会从最新的map中取出该资源url对应的修改时间,如果值晚于客户端请求的资源修改时间,这时会返回最新的已经修改过的资源给客户端。否则返回304 not modifed
这里记录资源修改时间的方式有etag及last_modified。最先有的是last_modified,它的工作方式就是上述介绍的,但缺点是只能精确到秒级别。也就是说当你在一秒中修改资源两次,而客户端拿到的是第一次修改,那之后就算客户端第二次再次请求也不会拿到最新的资源。
而etag的出现正是为了解决last_modified的秒级问题,于http 1.1被提出。
今天测试了下,在没有nginx等前端反向代理服务器时,tomcat竟然默认对静态资源做了缓存。
tomcat默认运用etag及last_modifed。etag与if_no_match(客户端浏览器上传时在http head中应该放的属性名)一起使用,last_modified与If-Modified-Since一起使用。
客户端首次请求时,得到请求响应数据如下:
GET http://localhost:8080/webTest/jsp/index.jsp [HTTP/1.1 200 OK 1ms]
GET http://localhost:8080/webTest/js/hello.js [HTTP/1.1 200 OK 1ms]
GET http://localhost:8080/webTest/img/a.jpg [HTTP/1.1 200 OK 2ms]
我们看一下Hello.js这个请求响应具体信息:
server Apache-Coyote/1.1 (表明服务器是tomcat)
Last-Modified: Sun, 11 May 2014 10:54:33 GMT
Etag: W/"175-1399805673000"
Date: Sun, 11 May 2014 10:59:23 GMT
Content-Type: application/javascript;charset=UTF-8
Content-Length: 175
Accept-Ranges: bytes
从上面可以看到tomcat即返回了last_modified也返回了etag。
客户端再次请求时,请求数据如下:
If-None-Match: W/"175-1399805673000"
If-Modified-Since: Sun, 11 May 2014 10:54:33 GMT
响应如下:
GET http://localhost:8080/webTest/jsp/index.jsp [HTTP/1.1 200 OK 1ms]
GET http://localhost:8080/webTest/js/hello.js [HTTP/1.1 304 Not Modified 1ms]
GET http://localhost:8080/webTest/img/a.jpg [HTTP/1.1 304 Not Modified 1ms]
从中我们可以看到tomcat对于静态数据作了缓存。
接着我们分析tomcat对于这部分静态缓存的判断处理,这部分逻辑是写在DefaultServlet类中,
我们可以在doGet方法中进入ServiceContext方法中找到以下源码:
// Check if the conditions specified in the optional If headers are
// satisfied.
if (cacheEntry.context == null) {
// Checking If headers
boolean included =
(request.getAttribute(Globals.INCLUDE_CONTEXT_PATH_ATTR) != null);
if (!included
&& !checkIfHeaders(request, response, cacheEntry.attributes)) { //这句判断是否需要返回整个资源请求
return;
}
}
上面源码的 if (!included
&& !checkIfHeaders(request, response, cacheEntry.attributes))
用于判断是否需要返回整个资源,如果indcluded与checkIfHeaders方法返回的都是false,这时就直接返回,说明资源未修改,或者是缓存不支持的请求方式。
我们接着查看checkIfHeaders方法:
/**
* Check if the conditions specified in the optional If headers are
* satisfied.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resourceAttributes The resource information
* @return boolean true if the resource meets all the specified conditions,
* and false if any of the conditions is not satisfied, in which case
* request processing is stopped
*/
protected boolean checkIfHeaders(HttpServletRequest request,
HttpServletResponse response,
ResourceAttributes resourceAttributes)
throws IOException {
return checkIfMatch(request, response, resourceAttributes)
&& checkIfModifiedSince(request, response, resourceAttributes)
&& checkIfNoneMatch(request, response, resourceAttributes)
&& checkIfUnmodifiedSince(request, response, resourceAttributes);
}
可以看到tomcat只有当这四个属性全部返回true(也就是说全部认为资源已经改变)才会返回true,这样最终会将整个资源(最新修改过的)返回客户端。
在这里,我们从上面实际过程当中看到,浏览器第二次请求资源时在http请求header中放了
If-None-Match: W/"175-1399805673000"
If-Modified-Since: Sun, 11 May 2014 10:54:33 GMT
这两个属性。
因此我们查看
&& checkIfModifiedSince(request, response, resourceAttributes)
&& checkIfNoneMatch(request, response, resourceAttributes)
这两个方法
checkIfModifiedSince源码如下:
/**
* Check if the if-modified-since condition is satisfied.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resourceInfo File object
* @return boolean true if the resource meets the specified condition,
* and false if the condition is not satisfied, in which case request
* processing is stopped
*/
protected boolean checkIfModifiedSince(HttpServletRequest request,
HttpServletResponse response,
ResourceAttributes resourceAttributes) {
try {
long headerValue = request.getDateHeader("If-Modified-Since");
long lastModified = resourceAttributes.getLastModified();
if (headerValue != -1) {
// If an If-None-Match header has been specified, if modified since
// is ignored.
if ((request.getHeader("If-None-Match") == null)
&& (lastModified < headerValue + 1000)) {
// The entity has not been modified since the date
// specified by the client. This is not an error case.
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
response.setHeader("ETag", resourceAttributes.getETag());
return false;
}
}
} catch (IllegalArgumentException illegalArgument) {
return true;
}
return true;
}
源码中可以看到:
if ((request.getHeader("If-None-Match") == null)
&& (lastModified < headerValue + 1000)) {
这句话表明只有在客户端浏览器发送的请求头中不包含If-None-Match,IfModifiedSince才会生效。
我们接着看checkIfNoneMatch,源码如下:
/**
* Check if the if-none-match condition is satisfied.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resourceInfo File object
* @return boolean true if the resource meets the specified condition,
* and false if the condition is not satisfied, in which case request
* processing is stopped
*/
protected boolean checkIfNoneMatch(HttpServletRequest request,
HttpServletResponse response,
ResourceAttributes resourceAttributes)
throws IOException {
String eTag = resourceAttributes.getETag();
String headerValue = request.getHeader("If-None-Match");
if (headerValue != null) {
boolean conditionSatisfied = false;
if (!headerValue.equals("*")) {
StringTokenizer commaTokenizer =
new StringTokenizer(headerValue, ",");
while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
String currentToken = commaTokenizer.nextToken();
if (currentToken.trim().equals(eTag))
conditionSatisfied = true;
}
} else {
conditionSatisfied = true;
}
if (conditionSatisfied) {
// For GET and HEAD, we should respond with
// 304 Not Modified.
// For every other method, 412 Precondition Failed is sent
// back.
if ( ("GET".equals(request.getMethod()))
|| ("HEAD".equals(request.getMethod())) ) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
response.setHeader("ETag", eTag);
return false;
}
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return false;
}
}
return true;
}
这里:
String eTag = resourceAttributes.getETag();
String headerValue = request.getHeader("If-None-Match");
这两句比较简单,就是分别从服务器缓存和http请求头中中取出etag。
接着判断这两个etag如果相等,则conditionSatisfied为true,会执行到以下语句:
if (conditionSatisfied) {
// For GET and HEAD, we should respond with
// 304 Not Modified.
// For every other method, 412 Precondition Failed is sent
// back.
if ( ("GET".equals(request.getMethod()))
|| ("HEAD".equals(request.getMethod())) ) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
response.setHeader("ETag", eTag);
return false;
}
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return false;
}
这段语句中可以发现,如果资源未改变的情况下,并且请求方式为GET或者HEAD时,会返回304状态码。否则返回一个412状态码,同样不会返回资源内容。
如果上述最终
if ((request.getHeader("If-None-Match") == null)
&& (lastModified < headerValue + 1000))
条件不成立,即资源更新了或者是第一次请求,这里会读取当前请求资源文件,并最终放入http响应中。
tomcat缓存静态资源深入的更多相关文章
- Tomcat 对静态资源的处理
Tomcat 中的请求都是由 Servlet 处理,静态资源也不例外.在默认的 web.xml 中,配置了一个 DefaultServlet 用于处理静态资源,它支持缓存和断点续传. DefaultS ...
- tips 前端 阻止 浏览器缓存静态资源
手机浏览器 uc上一直表现良好 qq浏览器还有微信上网址直接打开的(一样采用qq浏览器的内核) 大量缓存了静态资源 css js 图片 等这些当出现改动了刷新网页根本没有效果 电脑端浏览器没有问题 因 ...
- nginx缓存静态资源,只需几个配置提升10倍页面加载速度
nginx缓存静态资源,只需几个配置提升10倍页面加载速度 首先我们看图说话 这是在没有缓存的情况下,这个页面发送了很多静态资源的请求: 1.png 可以看到,静态资源占用了整个页面加载用时的90 ...
- ASP.NET Core缓存静态资源
背景 缓存样式表,JavaScript或图像文件等静态资源可以提高您网站的性能.在客户端,总是从缓存中加载一个静态文件,这样可以减少对服务器的请求数量,从而减少获取页面及其资源的时间.在服务器端,由于 ...
- Nginx+Tomcat动静态资源分离
1 创建用户.用户组 useradd -g users www passwd www //设置密码,否则该用户不可用 groupadd -g 888 www //创建用户组 gpasswd -a ww ...
- SpringBoot cache-control 配置静态资源缓存 (以及其中的思考经历)
昨天在部署项目时遇到一个问题,因为服务要部署到外网使用,中间经过了较多的网络传输限制,而且要加载arcgis等较大的文件,所以在部署后,发现页面loading需要很长时间,而且刷新也要重新从服务器下载 ...
- nginx静态资源缓存与压缩
一.静态资源缓存 参考文章 (1)apache设置max-age或expires 这里需要修改.htaccess文件. <IfModule mod_headers.c> <Files ...
- Nginx的静态资源缓存以及压缩
Nginx是一款轻量级的网页服务器.反向代理器以及电子邮件代理服务器.Nginx采用的是异步非阻塞的通信机制(epoll模型),支持更大的并发连接.所谓的epoll模型:当事件没有准备好时,就放入ep ...
- 静态资源(JS/CSS)存储在localStorage
一.简单了解SEO SEO由英文Search Engine Optimization缩写而来, 中文意译为“搜索引擎优化”.SEO是指从自然搜索结果获得网站流量的技术和过程. 搜索引擎不优化的网站分为 ...
随机推荐
- What algorithm to use to normalize someone's face on image
http://stackoverflow.com/questions/7066104/what-algorithm-to-use-to-normalize-someones-face-on-image
- 测试环境下将centos6.8升级到centos7的操作记录(转)
在测试环境下安装openstack,由于在centos6下安装openstack,针对源的问题有很多,安装起来很不顺利! 但是在centos7下安装却很顺利,所以考虑将服务器由centos6升级到ce ...
- RabbitMQ学习总结 第四篇:发布/订阅 Publish/Subscribe
目录 RabbitMQ学习总结 第一篇:理论篇 RabbitMQ学习总结 第二篇:快速入门HelloWorld RabbitMQ学习总结 第三篇:工作队列Work Queue RabbitMQ学习总结 ...
- mina学习
长连接表示一旦建立了链接,就可以长时间的保持双方的通讯,例如: socket链接,推送平台. 短链接表示建立链接,完成数据的交换之后,就断开链接,例如: http链接. mina 框架是对socket ...
- Oracle Flashback Technologies - 闪回查询
Oracle Flashback Technologies - 闪回查询 查看表中,某行数据的修改记录 #创建一个表,并插入和修改数据 SQL> create table y3(id )); T ...
- Oracle Flashback Technologies - 估算不同时间段闪回日志的产生量
Oracle Flashback Technologies - 估算不同时间段闪回日志的产生量 v$flashback_database_stat监控闪回数据的i/o开销的统计信息,根据之前的系统负载 ...
- FB面经prepare: Task Schedule
每种task都有冷却时间,比如task1执行后,要经过interval时间后才能再次执行,求总共所需时间. 用HashMap保存每一个task的下一次可以开始执行的最早时间 package TaskS ...
- java中的BigDecimal和String的相互转换
/*由数字字符串构造BigDecimal的方法 02.*设置BigDecimal的小数位数的方法 03.*/ 04.import java.math.BigDecimal; 05.//数字字符串 06 ...
- ui方案设计
最近在做一个ui,前端,为了引出创意,万事开头难,记录一下: 算是单页吧,一般都是左侧是导航,右边是内容,单页的话,要记录标签关闭,窗口区域. 操作的连惯性等.UI设计是个细活,需要深加研究,以后补充 ...
- [原创]java WEB学习笔记66:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,PrepareInterceptor拦截器,paramsPrepareParamsStack 拦截器栈
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...