Recently I came across a tricky problem about browser caching. We are working on an SaaS web project and users are identified by login names. Homepage can be viewed after login and is cache by cache directives. So if 2 accounts belong to different tenants log on the same computer without cache clearing, the second user is probably to see the wrong homepage (homepage of another tenant). This problem appears on Firefox and Chrome, but not on IE.
Then I google and found this on stackoverflow:
http://stackoverflow.com/questions/398068/ie-302-redirect-no-cache-header-problem
A quirk of IE is that when you redirect, the no-cache headers are re-added to the redirected request. Hence, in your case, your redirected request also sends the “no-cache” request header carried over from the POST request.
This explains why the problem doesn’t show on IE. The no-cache header will force the server to return the correct homepage, instead of 304 response code indicating using client-side cache.
There are also several differences about caching behavior between IE and Firefox. Check this:
http://blog.httpwatch.com/2008/10/15/two-important-differences-between-firefox-and-ie-caching/
这个其实应该是常识,只不过以前做的J2EE应用大部分是内网里跑的东西,所以性能上没什么问题。这次APIS由于有在外面用的可能,加上使用了一些比较大的javascript框架(Ext),所以性能问题瞬间窜了上来。
以前做的J2EE应用没有使用上达500K的框架,最多就是几十K的Prototype,所以没什么问题。一个页面一般也就几十K最多了。但这次还在开发中的APIS,由于还在用debug版本的库,所以单单Ext就膨胀到了一个多M,加上不知道是Struts还是Tomcat默认写入Response的cache-control: no cache,在远程用起来就很慢,一般一个页面需要十多秒种甚至更久,实在无法忍受。前几天集中解决了问题。
首先是Cache-Control的问题,Google了好一阵,没有什么直接配置的方法,只好自己抄了一个一个Filter,通过和web.xml里配置的配合勉强凑合着用。一般就是对*.do实施no-cache政策,其他需要缓存的img, js文件,统统加上长达两周的缓存期限。ETag实在不会用,就先用这个缓存策略吧。
Filter的代码:
- public class ResponseHeaderFilter implements Filter {
- FilterConfig fc;
- public void doFilter(ServletRequest req, ServletResponse res,
- FilterChain chain) throws IOException, ServletException {
- HttpServletResponse response = (HttpServletResponse) res;
-
- for (Enumeration e = fc.getInitParameterNames(); e.hasMoreElements();) {
- String headerName = (String) e.nextElement();
- response.addHeader(headerName, fc.getInitParameter(headerName));
- }
-
- chain.doFilter(req, response);
- }
- public void init(FilterConfig filterConfig) {
- this.fc = filterConfig;
- }
- public void destroy() {
- this.fc = null;
- }
- }
web.xml里的巧妙配置:
- <filter>
- <filter-name>NoCache</filter-name>
- <filter-class>apis.server.common.util.ResponseHeaderFilter</filter-class>
- <init-param>
- <param-name>Cache-Control</param-name>
- <param-value>no-cache, must-revalidate</param-value>
- </init-param>
- </filter>
- <filter>
- <filter-name>CacheForWeek</filter-name>
- <filter-class>apis.server.common.util.ResponseHeaderFilter</filter-class>
- <init-param>
- <param-name>Cache-Control</param-name>
- <param-value>max-age=604800, public</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>NoCache</filter-name>
- <url-pattern>*.do</url-pattern>
- </filter-mapping>
- <filter-mapping>
- <filter-name>CacheForWeek</filter-name>
- <url-pattern>/images/*</url-pattern>
- </filter-mapping>
- <filter-mapping>
- <filter-name>CacheForWeek</filter-name>
- <url-pattern>/img/*</url-pattern>
- </filter-mapping>
- <filter-mapping>
- <filter-name>CacheForWeek</filter-name>
- <url-pattern>/icons/*</url-pattern>
- </filter-mapping>
- <filter-mapping>
- <filter-name>CacheForWeek</filter-name>
- <url-pattern>/ext/*</url-pattern>
- </filter-mapping>
- <filter-mapping>
- <filter-name>CacheForWeek</filter-name>
- <url-pattern>*.js</url-pattern>
- </filter-mapping>
- <filter-mapping>
- <filter-name>CacheForWeek</filter-name>
- <url-pattern>*.css</url-pattern>
- </filter-mapping>
(插入一段:在探测这些性能问题的时候,我使用的是一个Firebug的插件,也就是Firefox插件的插件-YSlow,好像是Yahoo的,结合Firebug里XHR的Net这块做Profiling,效果很不错,很容易就知道瓶颈)
还有一个gzip的办法,就是在服务器压缩内容,再传给浏览器。现在主流的浏览器都支持gzip压缩,而且这些html和js文本压缩起来很厉害,基本上可以有40%的压缩率。办法在servel.xml的注释里也有写,就是在Connector元素里加上
compression=”on”
compressionMinSize=”2048″
noCompressionUserAgents=”gozilla,traviata”
compressableMimeType=”text/html,text/xml,text/javascript,text/css,text/plain”
以上的内容大部分都是Google得来,我自己做了一下整理
最近评论