tomcat缓存静态资源深入Apache

简介:

tomcat缓存静态资源深入Apache

之前看过APACH及nginx的对于静态资源(含JS,图片,CSS等)部分的缓存,用于加速并减轻后台实际网络服务器的压力。

静态资源缓存是WEB服务器优化的一种手段,基本原理如下:

1.客户端浏览器请求服务器一个服务(该服务含有图片,JS等静态资源),通常会对于每一个网页中的独立图片或JS文件发送一个HTTP请求

2.WEB服务器对于每个资源的HTTP请求进行解析,并生成一个资源修改时间的唯一值(可以是ETAG或LAST_MODIFIED参数),放入服务器端地图,键为资源URL,值为资源修改时间。最后将此资源修改时间的唯一值包含在http头上返回,因为是首次请求,所以会将所有内容放在http body中一并返回给客户浏览器端

3.客户浏览器接收服务器响应,并将服务器返回的资源修改时间作为key放入浏览器客户端,值为http body中的实际资源内容

4.客户浏览器再次请求静态资源时,会将资源修改时间一并发送给服务器

5.服务端会从最新的映射中取出该资源url对应的修改时间,如果值晚于客户端请求的资源修改时间,这时会返回最新的已经修改过的资源给客户端。体改

这里记录资源修改时间的方式有ETAG及LAST_MODIFIED。最先有的是LAST_MODIFIED,它的工作方式就是上述介绍的,但缺点是只能精确到秒级别。也就是说当你在一秒中修改资源两次,而客户端拿到的是第一次修改,那之后就算客户端第二次再次请求也不会拿到最新的资源。

而etag的出现正是为了解决last_modified的秒级问题,于http 1.1被提出。

 

今天测试了下,在没有nginx的等前端反向代理服务器时,Tomcat的竟然默认对静态资源做了缓存。

tomcat默认运用etag及last_modifed.etag与if_no_match(客户端浏览器上传时在http头中应该放的属性名)一起使用,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这个请求响应具体信息:

服务器Apache-Coyote / 1.1(表示服务器是tomcat)
Last-Modified:Sun,11 May 2014 10:54:33 GMT
Etag:W /“175-1399805673000”
日期:Sun,2014年5月11日10:59:23 GMT
内容-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,2014年5月11日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方法中找到以下源码:

//检查在可选If头中指定的条件是否
满足。
if(cacheEntry.context == null){

//检查如果标题
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方法返回的都是假的,这时就直接返回,说明资源未修改,或者是缓存不支持的请求方式。

我们接着查看checkIfHeaders方法:

/ **
*检查在可选If头中指定的条件是否
满足。
*
* @param请求我们正在处理的servlet请求
* @param response我们正在创建的servlet响应
* @param resourceAttributes资源信息
* @return布尔值如果资源满足所有指定条件,
则为true,如果任何条件不满足,在这种情况下
*请求处理停止
* /
protected boolean checkIfHeaders(HttpServletRequest请求,
HttpServletResponse响应,
ResourceAttributes resourceAttributes)
抛出IOException {

返回checkIfMatch(请求,响应resourceAttributes)
&& checkIfModifiedSince(请求,响应resourceAttributes)
&& checkIfNoneMatch(请求,响应resourceAttributes)
&& checkIfUnmodifiedSince(请求,响应resourceAttributes);

}

可以看到的Tomcat只有当这四个属性全部返回真(也就是说全部认为资源已经改变)才会返回真,这样最终会将整个资源(最新修改过的)返回客户端。

在这里,我们从上面实际过程当中看到,浏览器第二次请求资源时在HTTP请求报头中放了

If-None-Match:W /“175-1399805673000”
If-Modified-Since:Sun,2014年5月11日10:54:33 GMT

这两个属性。

因此我们查看

&& checkIfModifiedSince(request,response,resourceAttributes)
&& checkIfNoneMatch(request,response,resourceAttributes)

这两个方法

checkIfModifiedSince源码如下:

/ **
*检查if-modified-since条件是否满足。
*
* @param request我们正在处理的servlet请求
* @param response我们正在创建的servlet响应
* @param resourceInfo File对象
* @return布尔值如果资源满足指定的条件,
则为true,如果条件不满足则为false,在这种情况下,请求
*处理被停止
* /
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-None-Match标头,如果自
//被忽略后被修改
如果((request.getHeader(“If-None-Match”)== null)
&&(lastModified <headerValue + 1000)){
//实体自
客户端指定的日期起未被修改这不是一个错误的情况。
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
response.setHeader(“ETag”,resourceAttributes.getETag());

返回false;
}
}
} catch(IllegalArgumentException illegalArgument){
return true;
}
返回true;

}

源码中可以看到:

if((request.getHeader(“If-None-Match”)== null)
&&(lastModified <headerValue + 1000)){

这句话表明只有在客户端浏览器发送的请求头中不包含如果无 - 匹配,IfModifiedSince才会生效。

我们接着看checkIfNoneMatch,源码如下:

/ **
*检查是否满足if-none-match条件。
*
* @param request我们正在处理的servlet请求
* @param response我们正在创建的servlet响应
* @param resourceInfo File对象
* @return布尔值如果资源满足指定条件,
则为true,如果条件不满足,则为false,在这种情况下,请求
*处理已停止
* /
protected boolean checkIfNoneMatch(HttpServletRequest请求,
HttpServletResponse响应,
ResourceAttributes resourceAttributes)
抛出IOException {

String eTag = resourceAttributes.getETag();
String headerValue = request.getHeader(“If-None-Match”);
if(headerValue!= null){

布尔型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){

//对于GET和HEAD,我们应该使用
// 304 Not Modified进行响应
//对于其他方法,412先决条件失败发送/ /
返回。
如果((“GET”.equals(request.getMethod()))
||(“HEAD”.equals(request.getMethod()))){
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
response.setHeader(“ETag”,eTag);

返回false;
}
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
返回false;
}
}
返回true;

}

这里:

String eTag = resourceAttributes.getETag();
String headerValue = request.getHeader(“If-None-Match”);

这两句比较简单,就是分别从服务器缓存和HTTP请求头中中取出ETAG。

接着判断这两个ETAG如果相等,则conditionSatisfied为真,会执行到以下语句:

if(conditionSatisfied){

//对于GET和HEAD,我们应该使用
// 304 Not Modified进行响应
//对于其他方法,412先决条件失败发送/ /
返回。
如果((“GET”.equals(request.getMethod()))
||(“HEAD”.equals(request.getMethod()))){
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
response.setHeader(“ETag”,eTag);

返回false;
}
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
返回false;
}

这段语句中可以发现,如果资源未改变的情况下,并且请求方式为GET或者HEAD时,会返回304状态码。否则返回一个412状态码,同样不会返回资源内容。

如果上述最终

if((request.getHeader(“If-None-Match”)== null)
&&(lastModified <headerValue + 1000))

条件不成立,即资源更新了或者是第一次请求,这里会读取当前请求资源文件,并最终放入HTTP响应中。

原文地址http://www.bieryun.com/1697.html

相关文章
|
7月前
|
Java 应用服务中间件 Apache
从零手写实现 apache Tomcat-01-入门介绍
创建简易Tomcat涉及理解其作为Java服务器的角色,建立HTTP服务器,实现Servlet接口处理动态和静态内容,以及启动和关闭服务器。项目mini-cat是一个简化版Tomcat实现,支持Servlet、静态网页和基础功能。可通过maven添加依赖并运行测试类快速体验。开源项目位于[GitHub](https://github.com/houbb/minicat)。
|
7月前
|
Java 应用服务中间件 Apache
简介Nginx,Tomcat和 Apache
简介Nginx,Tomcat和 Apache
120 2
简介Nginx,Tomcat和 Apache
|
4月前
|
Ubuntu Java 应用服务中间件
如何通过 Apt-Get 在 Ubuntu 14.04 上安装 Apache Tomcat 7
如何通过 Apt-Get 在 Ubuntu 14.04 上安装 Apache Tomcat 7
100 0
|
2月前
apache+tomcat配置多站点集群的方法
apache+tomcat配置多站点集群的方法
37 4
|
2月前
|
负载均衡 应用服务中间件 Apache
Tomcat负载均衡原理详解及配置Apache2.2.22+Tomcat7
Tomcat负载均衡原理详解及配置Apache2.2.22+Tomcat7
48 3
|
4月前
|
Java 应用服务中间件 Apache
使用IDEA修改Web项目访问路径,以及解决Apache Tomcat控制台中文乱码问题
本文介绍了在IntelliJ IDEA中修改Web项目访问路径的步骤,包括修改项目、模块、Artifacts的配置,编辑Tomcat服务器设置,以及解决Apache Tomcat控制台中文乱码问题的方法。
261 0
使用IDEA修改Web项目访问路径,以及解决Apache Tomcat控制台中文乱码问题
|
4月前
|
缓存 前端开发 Java
【Azure 应用服务】App Service 使用Tomcat运行Java应用,如何设置前端网页缓存的相应参数呢(-Xms512m -Xmx1204m)?
【Azure 应用服务】App Service 使用Tomcat运行Java应用,如何设置前端网页缓存的相应参数呢(-Xms512m -Xmx1204m)?
|
4月前
|
Ubuntu Java 应用服务中间件
在Ubuntu 16.04上安装Apache Tomcat 8的方法
在Ubuntu 16.04上安装Apache Tomcat 8的方法
41 0
|
4月前
|
安全 Java 应用服务中间件
在CentOS 7上安装Apache Tomcat 8的方法
在CentOS 7上安装Apache Tomcat 8的方法
155 0
|
4月前
|
安全 Java 应用服务中间件
如何通过 Yum 在 CentOS 7 上安装 Apache Tomcat 7
如何通过 Yum 在 CentOS 7 上安装 Apache Tomcat 7
219 0

推荐镜像

更多