整体机制:多个业务tomcat 多个图片tomcat 通过nginx 实现负载均衡,服务器部分缓存已经完成。现在需要实现客户端缓存。
1 设置nginx 缓存机制
location ~ /photoserver/.*\.(gif|jpg|png|htm|jpeg|bmp|html|flv|ico|swf|doc)(.*) {
#root nginx_cache/cache;
if (!-f $request_filename) {
proxy_pass http://img.dahongwa.com; #图片服务器
}
proxy_cache_use_stale error timeout invalid_header http_500 http_502 http_503 http_504 http_404;
proxy_cache_methods GET HEAD POST;
proxy_cache_min_uses 1;
expires max;
access_log logs/img.dahongwa.com.log;
}
2 配置图片tomcat过滤器
public class ResponseHeaderFilter implements Filter {
/** The fc. */
FilterConfig fc;
/*
* (non-Javadoc)
*
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
* javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
// set the provided HTTP response parameters
for (Enumeration e = fc.getInitParameterNames(); e.hasMoreElements();) {
String headerName = (String) e.nextElement();
response.setHeader(headerName, fc.getInitParameter(headerName));
}
// pass the request/response on
chain.doFilter(req, response);
}
/*
* (non-Javadoc)
*
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
public void init(FilterConfig filterConfig) {
this.fc = filterConfig;
}
/*
* (non-Javadoc)
*
* @see javax.servlet.Filter#destroy()
*/
public void destroy() {
this.fc = null;
}
配置图片tomcat web.xml文件
<filter>
<filter-name>ResponseHeaderFilter</filter-name>
<filter-class>com.legendshop.core.photo.ResponseHeaderFilter</filter-class>
<init-param>
<param-name>Cache-Control</param-name>
<param-value>max-age=315360000</param-value>
</init-param>
<init-param>
<param-name>Access-Control-Max-Age</param-name>
<param-value>315360000</param-value>
</init-param>
<init-param>
<param-name>Expires</param-name>
<param-value>315360000</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ResponseHeaderFilter</filter-name>
<url-pattern>/photoserver/photo/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>ResponseHeaderFilter</filter-name>
<url-pattern>/photoserver/images/*</url-pattern>
</filter-mapping>
3 在图片tmcat配置 过滤器 ETagContentFilter
<filter>
<filter-name>ETagContentFilter</filter-name>
<filter-class>com.legendshop.core.photo.ETagContentFilter</filter-class>
<init-param>
<param-name>Cache-Control</param-name>
<param-value>max-age=315360000</param-value>
</init-param>
<init-param>
<param-name>Access-Control-Max-Age</param-name>
<param-value>315360000</param-value>
</init-param>
<init-param>
<param-name>Expires</param-name>
<param-value>315360000</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ETagContentFilter</filter-name>
<url-pattern>/photoserver/photo/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>ETagContentFilter</filter-name>
<url-pattern>/photoserver/images/*</url-pattern>
</filter-mapping>
public class ETagContentFilter implements Filter {
private static Logger logger = LoggerFactory.getLogger(ETagContentFilter.class);
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest servletRequest = (HttpServletRequest) req;
HttpServletResponse servletResponse = (HttpServletResponse) res;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ETagResponseWrapper wrappedResponse = new ETagResponseWrapper(servletResponse, baos);
chain.doFilter(servletRequest, wrappedResponse);
byte [] bytes = baos.toByteArray();
String token = '"' + ETagComputeUtils.getMd5Digest(bytes) + '"' ;
servletResponse.setHeader( "ETag" , token); // always store the ETag in the header
String previousToken = servletRequest.getHeader("If-None-Match");
if (previousToken != null && previousToken.equals(token)) { // compare previous token with current one
logger.debug("ETag match: returning 304 Not Modified");
servletResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
// use the same date we sent when we created the ETag the first time through
servletResponse.setHeader("Last-Modified",servletRequest.getHeader("If-Modified-Since"));
// servletResponse.setHeader("Cache-Control",315360000);
//Cache-Control: max-age=0 + Last-Modified(ETag)
}else{ // first time through - set last modified time to now
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MILLISECOND, 0 );
Date lastModified = cal.getTime();
servletResponse.setDateHeader("Last-Modified", lastModified.getTime());
logger.debug( " Writing body content " );
servletResponse.setContentLength(bytes.length);
ServletOutputStream sos = servletResponse.getOutputStream();
sos.write(bytes);
sos.flush();
sos.close();
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
public class ETagResponseWrapper extends HttpServletResponseWrapper
{
private HttpServletResponse response = null;
private ServletOutputStream stream = null;
private PrintWriter writer = null;
private OutputStream buffer = null;
public ETagResponseWrapper( HttpServletResponse response, OutputStream buffer )
{
super( response );
this.response = response;
this.buffer = buffer;
}
public ServletOutputStream getOutputStream() throws IOException
{
if( stream == null )
stream = new ETagResponseStream( response, buffer );
return stream;
}
public PrintWriter getWriter() throws IOException
{
if( writer == null )
writer = new PrintWriter( new OutputStreamWriter( getOutputStream(), "UTF-8" ) );
return writer;
}
public void flushBuffer() throws IOException
{
stream.flush();
}
public class ETagResponseStream extends ServletOutputStream{
public ETagResponseStream(HttpServletResponse response,OutputStream outputstream)throws IOException
{
closed = false;
stream = null;
stream = outputstream;
}
public void close()
throws IOException
{
if(!closed)
{
stream.close();
closed = true;
}
}
public void flush()
throws IOException
{
if(!closed)
stream.flush();
}
public void write(int i)
throws IOException
{
if(!closed)
stream.write((byte)i);
}
public void write(byte abyte0[], int i, int j)
throws IOException
{
if(!closed)
stream.write(abyte0, i, j);
}
public void write(byte abyte0[])
throws IOException
{
write(abyte0, 0, abyte0.length);
}
public boolean closed()
{
return closed;
}
public void reset()
{
}
private boolean closed;
private OutputStream stream;
}
好了 下面你刷新就是一堆的304了,去玩吧。