项目中有一个公共页面mian.jsp,里面有大量的javascript代码,需要将他分离出来,尽可能的利用浏览器的缓存。一些常规的java脚本只需要放到js文件中就行,还有一些javascript代码是动态的(也就是说会有一些jstl标签代码)就不能放到js文件中了。我采取的方案是将这些含有jstl标签的javascript脚本代码放入到一个gsp文件中,然后在页面中引用。例如:
<script type="text/javascript" src="${staticurl}/js/userAutoTips.jsp"></script>
显然这种方式仅仅是实现了javascript脚本的分离,这种页面默认是不缓存的,所以每次需要的时候仍然会请求信息。要想让服务器缓存这些页面,需要做点手脚。
首先我们来看一下Http的缓存。
1、ETag
Http1.1中没有规定ETag的格式和生成方式,只要求用双引号括起来。因此可以用MD5,或者直接用文件的最后修改时间。Tomcat就采用 文件大小 + "-" + 文件最后修改时间的毫秒数 来生成ETag。
Etag 与 If-None-Match 对应。
第一次请求时,服务端响应头中有ETag(红色下划线 所示)。
第二次请求时,请求头中就带有If-None-Match(红色下划线 所示)。服务器判断请求头中 If-None-Match 与服务端计算的ETag是否一致,如果一致就表示没有更新,返回304,否则按第一次请求时处理。
2、Last-Modified
Last-Modified与If-Modified-Since对应。
第一次请求时,响应头中带有 Last-Modified(蓝色下划线 所示),格式如:Wed, 22 Jul 2009 07:08:07 GMT,是零时区的 GMT 时间。
第二次请求时,请求头中就带有If-Modified-Since(蓝色下划线 所示)。服务器判断请求头中If-Modified-Since与请求资源的最后修改时间是否一致,如果一致就表示资源没有更新,返回304,否则按第一次请求时处理。
3、Cache-Control
Cache-Control是一个集合属性,可以包含多个子属性。
第一次请求时,响应头中带有Cache-Control(红色下划线 所示)。
第二次请求时,浏览器判断Cache-Control,如果是max-age并且其值有效(在有效的缓存时间内),浏览器直接使用本地缓存,不向服务端发送请求,否则按第一次请求处理;如果是no-cache和no-store,不缓存,每次都是重新向服务端请求。
4、Expires
第一次请求时,响应头中带有 Expires(蓝色下划线 所示),格式如:Wed, 22 Jul 2009 07:08:07 GMT,是零时区的 GMT 时间。
第二次请求时,浏览器判断Expires,如果Expires是有效的,浏览器直接使用本地缓存,不向服务端发送请求,否则按第一次请求处理。
说明: 在firebug中,使用本地缓存时,资源名称会变成灰色 。
public static boolean checkHeaderCache(long adddays, long modelLastModifiedDate, HttpServletRequest request, HttpServletResponse response) { // com.jdon.jivejdon.presentation.filter.ExpiresFilter request.setAttribute("myExpire", adddays); // convert seconds to ms. long adddaysM = adddays * 1000; long header = request.getDateHeader("If-Modified-Since"); long now = System.currentTimeMillis(); if (header > 0 && adddaysM > 0) { if (modelLastModifiedDate > header) { // adddays = 0; // reset response.setStatus(HttpServletResponse.SC_OK); return true; } if (header + adddaysM > now) { // during the period happend modified response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return false; } } // if over expire data, see the Etags; // ETags if ETags no any modified String previousToken = request.getHeader("If-None-Match"); if (previousToken != null && previousToken.equals( Long.toString(modelLastModifiedDate))) { // not modified response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return false; } // if th model has modified , setup the new modified date response.setHeader("ETag", Long.toString(modelLastModifiedDate)); setRespHeaderCache(adddays, request, response); return true; } public static boolean setRespHeaderCache(long adddays, HttpServletRequest request, HttpServletResponse response) { request.setAttribute("myExpire", adddays); long adddaysM = adddays * 1000; String maxAgeDirective = "max-age=" + adddays; response.setHeader("Cache-Control", maxAgeDirective); response.setStatus(HttpServletResponse.SC_OK); response.addDateHeader("Last-Modified", System.currentTimeMillis()); response.addDateHeader("Expires", System.currentTimeMillis() + adddaysM); return true; }
通过客户端http缓存 服务器端缓存 这些技术综合在一起,可以大大增强每台服务器抗高负载能力,把带宽留给更需要实时更新的模型或页面。
这 种方式比静态化页面的好处是:精确性,静态页面.html比如Apache中mod_expire设置,如设置expire为十分钟,如果过了十分钟,其 实页面内容还是没有变,也要加载静态页面到客户端,而结合Etag的动态页面,不但起到静态页面一样的效果(其实经过Apache处理的静态页面html 也是动态页面),而且可以做到精确制导,就象多国部队轰炸利比亚一样,精确打击。这是灵活性的魅力。
5、附录
1、Cache-Control和Expires对F5和Ctrl+F5无效。当按F5刷新或按Ctrl+F5强制刷新时,IE和Firefox都会忽视本地缓存,重新向服务端发起请求
。
刷新 F5 Cache-Control: max-age=0强制刷新 Ctrl+F5 Pragma: no-cache |
刷新 F5If-Modified-Since: Sun, 21 Nov 2004 14:35:21 GMT强制刷新 Ctrl+F5 Cache-Control: no-cache |