整体机制:多个业务tomcat 多个图片tomcat 通过nginx 实现负载均衡,服务器部分缓存已经完成。现在需要实现客户端缓存。
1 设置nginx 缓存机制
请尊重知识,请尊重原创 更多资料参考请见 http://www.cezuwang.com/listFilm?page=1&areaId=906&filmTypeId=1
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了,去玩吧。