摘要:在开发趣得网(有关地区性商贸信息搜索与发布的网站) 的实践中,由于网页打开的速度较慢。查看了很多关于怎样。提高网页打开速度的资料。总结了一下。
关键词:趣得 ,免费发布,商贸信息,Http请求,网页打开速度
下面这段英文引用自:提升网站速度的13条规则
Web page designs are getting richer and richer, which means more scripts, stylesheets, images, and Flash in the page. A first-time visitor to your page may have to make several HTTP requests, but by using the Expires header you make those components cacheable. This avoids unnecessary HTTP requests on subsequent page views. Expires headers are most often used with images, but they should be used on all components including scripts, stylesheets, and Flash components.
Browsers (and proxies) use a cache to reduce the number and size of HTTP requests, making web pages load faster. A web server uses the Expires header in the HTTP response to tell the client how long a component can be cached. This is a far future Expires header, telling the browser that this response won't be stale until April 15, 2010.
Expires: Thu, 15 Apr 2010 20:00:00 GMT
If your server is Apache, use the ExiresDefault directive to set an expiration date relative to the current date. This example of the ExpiresDefault directive sets the Expires date 10 years out from the time of the request.
ExpiresDefault "access plus 10 years"
Keep in mind, if you use a far future Expires header you have to change the component's filename whenever the component changes. At Yahoo! we often make this step part of the build process: a version number is embedded in the component's filename, for example, yahoo_2.0.6.js.
Using a far future Expires header affects page views only after a user has already visited your site. It has no effect on the number of HTTP requests when a user visits your site for the first time and the browser's cache is empty. The impact of this performance improvement depends, therefore, on how often users hit your pages with a primed cache. (A "primed cache" already contains all of the components in the page.) We measured this at Yahoo! and found the number of page views with a primed cache is 75-85%. By using a far future Expires header, you increase the number of components that are cached by the browser and re-used on subsequent page views without sending a single byte over the user's Internet connection.
具体实施:
1、写一个过滤器,针对请求所静态内容的文件名后缀,给response 设置一个很长的过期时间,给所有的静态文件名称加上一个版本号。
过滤器实现可以参考
kenny 写的代码:《功能强大的 Servlet Filter 完整源代码》
/*
* <p>Company: 凌科软件 www.elingke.com </p>
* @author liubaojun
* @version 1.0
* Created on 2004-11-29
* 来源于 elinkBSP 部分源代码
*/
package com.elink.control;
import Java.io.*;
import java.util.*;
import java.util.zip.*;
import javax.Servlet.*;
import javax.servlet.http.*;
//import com.elink.util.*;
/**
* @author liubj
*/
public class BusiFilter implements Filter
{
private FilterConfig config = null;
private HashMap expiresMap = null;
private String encoding = null;
public void init(FilterConfig filterConfig)
{
this.config = filterConfig;
this.encoding = config.getInitParameter("encoding");
if( encoding == null || encoding.length() == 0 )
{
encoding = "GBK";
}
expiresMap = new HashMap();
Enumeration names = config.getInitParameterNames();
while( names.hasMoreElements() )
{
String paramName = (String)names.nextElement();
if( !"encoding".equals(paramName) )
{
String paramValue = config.getInitParameter(paramName);
try
{
Integer expires = Integer.valueOf( config.getInitParameter(paramName) );
expiresMap.put(paramName, expires);
}
catch( Exception ex)
{
//LogUtil.logError( "Filter."+paramValue+"="+paramValue);
}
}
}
}
public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
// 汉字问题
HttpServletRequest httpRequest = (HttpServletRequest)request;
httpRequest.setCharacterEncoding( encoding );
// chain.doFilter(request, response);
// 压缩传输
HttpServletResponse httpResponse = (HttpServletResponse)response;
String uri = httpRequest.getRequestURI();
String transferEncoding = getGZIPEncoding((HttpServletRequest)request);
if (transferEncoding == null)
{
setResponseHeader( httpResponse, uri, transferEncoding );
chain.doFilter(request, response);
}
else
{
if( uri.endsWith(".ctl") || uri.endsWith("busictrl") ) // 不处理的 URL
{
chain.doFilter(request, response);
}
else
{
setResponseHeader( httpResponse, uri, transferEncoding );
httpResponse.setHeader("Content-Encoding", transferEncoding);
GZIPEncodableResponse wrappedResponse = new GZIPEncodableResponse((HttpServletResponse)response);
chain.doFilter(request, wrappedResponse);
wrappedResponse.flush();
}
}
}
public void destroy()
{
}
private void setResponseHeader( HttpServletResponse response, String uri, String transferEncoding )
{
//LogUtil.logDebug( uri + ".Accept-Encoding: "+ transferEncoding);
String ext = null;
int dot = uri.lastIndexOf(".");
if( dot != -1 )
{
ext = uri.substring( dot+1 );
}
if( ext!= null && ext.length() > 0 )
{
Integer expires = (Integer)expiresMap.get(ext);
if( expires != null )
{
//LogUtil.logDebug( uri + ".Expires: "+ expires.intValue());
if( expires.intValue() > 0 )
{
response.setHeader("Cache-Control","max-age="+expires.intValue()); //HTTP 1.1
}
else
{
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache"); //HTTP 1.0
response.setDateHeader ("Expires", expires.intValue() );
}
}
}
}
private static String getGZIPEncoding(HttpServletRequest request)
{
String acceptEncoding = request.getHeader("Accept-Encoding");
if (acceptEncoding == null)
return null;
acceptEncoding = acceptEncoding.toLowerCase();
if (acceptEncoding.indexOf("x-gzip") >= 0)
{
return "x-gzip";
}
if (acceptEncoding.indexOf("gzip") >= 0)
{
return "gzip";
}
return null;
}
private class GZIPEncodableResponse extends HttpServletResponseWrapper
{
private GZIPServletStream wrappedOut;
public GZIPEncodableResponse(HttpServletResponse response) throws IOException
{
super(response);
wrappedOut = new GZIPServletStream(response.getOutputStream());
}
public ServletOutputStream getOutputStream() throws IOException
{
return wrappedOut;
}
private PrintWriter wrappedWriter;
public PrintWriter getWriter() throws IOException
{
if (wrappedWriter == null)
{
wrappedWriter = new PrintWriter(new OutputStreamWriter(
getOutputStream(), getCharacterEncoding()));
}
return wrappedWriter;
}
public void flush() throws IOException
{
if (wrappedWriter != null)
{
wrappedWriter.flush();
}
wrappedOut.finish();
}
}
private class GZIPServletStream extends ServletOutputStream
{
private GZIPOutputStream outputStream;
public GZIPServletStream(OutputStream source) throws IOException
{
outputStream = new GZIPOutputStream(source);
}
public void finish() throws IOException
{
outputStream.finish();
}
public void write(byte[] buf) throws IOException
{
outputStream.write(buf);
}
public void write(byte[] buf, int off, int len) throws IOException
{
outputStream.write(buf, off, len);
}
public void write(int c) throws IOException
{
outputStream.write(c);
}
public void flush() throws IOException
{
outputStream.flush();
}
public void close() throws IOException
{
outputStream.close();
}
}
}
配置文件
<filter>
<filter-name>busifilter</filter-name>
<filter-class>com.elink.control.BusiFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
<init-param>
<param-name>js</param-name>
<param-value>3600</param-value>
</init-param>
<init-param>
<param-name>gif</param-name>
<param-value>3600</param-value>
</init-param>
<init-param>
<param-name>jpg</param-name>
<param-value>3600</param-value>
</init-param>
<init-param>
<param-name>css</param-name>
<param-value>3600</param-value>
</init-param>
<init-param>
<param-name>bo</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>JSP</param-name>
<param-value>0</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>busifilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2、当内容更改或者升级了,就请求新版本的静态文件