码迷,mamicode.com
首页 > 编程语言 > 详细

获取返回浏览器的内容 —— Java 缓存的使用

时间:2015-10-23 12:03:35      阅读:279      评论:0      收藏:0      [点我收藏+]

标签:memcached   缓存   java   性能优化   

零. 引言

为什么使用缓存? 当网站流量逐渐增大, 数据库 IO 将比较早出现瓶颈, 而使用缓存, 可以使数据库瓶颈晚点到来, 从而提升网站性能。 Java Web 项目如何使用缓存? 缓存首先是要获取返回给页面的内容, 然后写入缓存(MemCached、 Redis等缓存), 本文使用 MemCached 作为示例。



二. 代码示例

要截获页面返回的内容,整体的思路是先把原始返回的页面内容写入到一个字符Writer,然后再组装成字符串并进行分析,最后再返回给客户端。代码如下:

用于包装 ServletOutputStream:

public class FilterServletOutputStream extends ServletOutputStream{
    // 不需要太多方法, 可以用 OutputStream 代替
    private DataOutputStream stream;

    public FilterServletOutputStream(OutputStream output) {
        stream = new DataOutputStream(output);
    }

    public void write(int b) throws IOException {
        stream.write(b);
    }

    public void write(byte[] b) throws IOException {
        stream.write(b);
    }

    public void write(byte[] b, int off, int len) throws IOException {
        stream.write(b, off, len);
    }

}



获取内容包装类:

import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by zhuyb on 15/10/21.
 */
public class MyHttpServletResponseWrapper extends HttpServletResponseWrapper implements Serializable{

    private static final long serialVersionUID = -7207188787151521963L;

    private static final Logger LOG = LoggerFactory.getLogger(MyHttpServletResponseWrapper.class);

    private int statusCode = SC_OK;
    private int contentLength;
    private String contentType;
    private final Map<String, List<Serializable>> headersMap = new TreeMap<String, List<Serializable>>(String.CASE_INSENSITIVE_ORDER);
    private final List cookies = new ArrayList();
    private ByteArrayOutputStream outstr;
    private PrintWriter writer;
    private boolean disableFlushBuffer = true;

    /**
     * Creates a GenericResponseWrapper
     */
    public MyHttpServletResponseWrapper(final HttpServletResponse response) {
        super(response);
        // 输出 HTML 结果的地方
        this.outstr = new ByteArrayOutputStream();
    }

    // 获取数据
    public byte[] getData() {
        if (writer != null)
            writer.flush();
        return outstr.toByteArray();
    }

    /**
     * Gets the outputstream.
     */
    public ServletOutputStream getOutputStream() {
        return new FilterServletOutputStream(outstr);
    }

    /**
     * Sets the status code for this response.
     */
    public void setStatus(final int code) {
        statusCode = code;
        super.setStatus(code);
    }

    /**
     * Send the error. If the response is not ok, most of the logic is bypassed and the error is sent raw
     * Also, the content is not cached.
     *
     * @param i      the status code
     * @param string the error message
     * @throws IOException
     */
    public void sendError(int i, String string) throws IOException {
        statusCode = i;
        super.sendError(i, string);
    }

    /**
     * Send the error. If the response is not ok, most of the logic is bypassed and the error is sent raw
     * Also, the content is not cached.
     *
     * @param i the status code
     * @throws IOException
     */
    public void sendError(int i) throws IOException {
        statusCode = i;
        super.sendError(i);
    }

    /**
     * Send the redirect. If the response is not ok, most of the logic is bypassed and the error is sent raw.
     * Also, the content is not cached.
     *
     * @param string the URL to redirect to
     * @throws IOException
     */
    public void sendRedirect(String string) throws IOException {
        statusCode = HttpServletResponse.SC_MOVED_TEMPORARILY;
        super.sendRedirect(string);
    }

    /**
     * Returns the status code for this response.
     */
    public int getStatus() {
        return statusCode;
    }

    /**
     * Sets the content length.
     */
    public void setContentLength(final int length) {
        this.contentLength = length;
        super.setContentLength(length);
    }

    /**
     * Gets the content length.
     */
    public int getContentLength() {
        return contentLength;
    }

    /**
     * Sets the content type.
     */
    public void setContentType(final String type) {
        this.contentType = type;
        super.setContentType(type);
    }

    /**
     * Gets the content type.
     */
    public String getContentType() {
        return contentType;
    }

    /**
     * Gets the print writer. 最终使用输出流写回浏览器
     */
    public PrintWriter getWriter() throws IOException {
        if (writer == null) {
            writer = new PrintWriter(new OutputStreamWriter(getOutputStream(), getCharacterEncoding()), true);
        }
        return writer;
    }

    /**
     * @see javax.servlet.http.HttpServletResponseWrapper#addHeader(java.lang.String, java.lang.String)
     */
    @Override
    public void addHeader(String name, String value) {
        List<Serializable> values = this.headersMap.get(name);
        if (values == null) {
            values = new LinkedList<Serializable>();
            this.headersMap.put(name, values);
        }
        values.add(value);

        super.addHeader(name, value);
    }

    /**
     * @see javax.servlet.http.HttpServletResponseWrapper#setHeader(java.lang.String, java.lang.String)
     */
    @Override
    public void setHeader(String name, String value) {
        final LinkedList<Serializable> values = new LinkedList<Serializable>();
        values.add(value);
        this.headersMap.put(name, values);

        super.setHeader(name, value);
    }

    /**
     * @see javax.servlet.http.HttpServletResponseWrapper#addDateHeader(java.lang.String, long)
     */
    @Override
    public void addDateHeader(String name, long date) {
        List<Serializable> values = this.headersMap.get(name);
        if (values == null) {
            values = new LinkedList<Serializable>();
            this.headersMap.put(name, values);
        }
        values.add(date);

        super.addDateHeader(name, date);
    }

    /**
     * @see javax.servlet.http.HttpServletResponseWrapper#setDateHeader(java.lang.String, long)
     */
    @Override
    public void setDateHeader(String name, long date) {
        final LinkedList<Serializable> values = new LinkedList<Serializable>();
        values.add(date);
        this.headersMap.put(name, values);

        super.setDateHeader(name, date);
    }

    /**
     * @see javax.servlet.http.HttpServletResponseWrapper#addIntHeader(java.lang.String, int)
     */
    @Override
    public void addIntHeader(String name, int value) {
        List<Serializable> values = this.headersMap.get(name);
        if (values == null) {
            values = new LinkedList<Serializable>();
            this.headersMap.put(name, values);
        }
        values.add(value);

        super.addIntHeader(name, value);
    }

    /**
     * @see javax.servlet.http.HttpServletResponseWrapper#setIntHeader(java.lang.String, int)
     */
    @Override
    public void setIntHeader(String name, int value) {
        final LinkedList<Serializable> values = new LinkedList<Serializable>();
        values.add(value);
        this.headersMap.put(name, values);

        super.setIntHeader(name, value);
    }

    /**
     * Adds a cookie.
     */
    public void addCookie(final Cookie cookie) {
        cookies.add(cookie);
        super.addCookie(cookie);
    }

    /**
     * Gets all the cookies.
     */
    public Collection getCookies() {
        return cookies;
    }

    /**
     * Flushes buffer and commits response to client.
     */
    public void flushBuffer() throws IOException {
        flush();

        // doing this might leads to response already committed exception
        // when the PageInfo has not yet built but the buffer already flushed
        // Happens in Weblogic when a servlet forward to a JSP page and the forward
        // method trigger a flush before it forwarded to the JSP
        // disableFlushBuffer for that purpose is 'true' by default
        // EHC-447
        if (!disableFlushBuffer) {
            super.flushBuffer();
        }
    }

    /**
     * Resets the response.
     */
    public void reset() {
        super.reset();
        cookies.clear();
        headersMap.clear();
        statusCode = SC_OK;
        contentType = null;
        contentLength = 0;
    }

    /**
     * Resets the buffers.
     */
    public void resetBuffer() {
        super.resetBuffer();
    }

    /**
     * Flushes all the streams for this response.
     */
    public void flush() throws IOException {
        if (writer != null) {
            writer.flush();
        }
        outstr.flush();
    }

    /**
     * Is the wrapped reponse's buffer flushing disabled?
     * @return true if the wrapped reponse's buffer flushing disabled
     */
    public boolean isDisableFlushBuffer() {
        return disableFlushBuffer;
    }

    /**
     * Set if the wrapped reponse's buffer flushing should be disabled.
     * @param disableFlushBuffer true if the wrapped reponse's buffer flushing should be disabled
     */
    public void setDisableFlushBuffer(boolean disableFlushBuffer) {
        this.disableFlushBuffer = disableFlushBuffer;
    }
}



缓存过滤器:

public class CacheFilter implements Filter {
public void destroy() {
}

public void init(FilterConfig filterConfig) throws ServletException {
}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;

String fullUrl = req.getRequestURL().toString();

// 带上 debug 参数不使用缓存, 方便后续开发调试
if (req.getParameter("debug") != null) {
    cacheble = false;
    MemcachedCacheManager.getCache("someproject_page_cache").put(key, "");
}

String key = fullUrl.replaceAll("(\\?|&)debug=1", "").hashCode();
// 如果页面路径包含 wenniuwuren 则缓存
boolean cacheble = “http://blog.csdn.net/wenniuwuren”.contains(“wenniuwuren");
MemCached pageCache = MemcachedCacheManager.getCache("jobmd_page_cache");
if (cacheble) {

    if (pageCache != null) {
        String html = (String) pageCache.get(key);
        if (StringUtils.isNotBlank(html)) {
            res.getWriter().write(html);
            return;
        }
        // 使用我们自定义的响应包装器来包装原始的 ServletResponse
        MyHttpServletResponseWrapper myHttpServletResponseWrapper = new MyHttpServletResponseWrapper(res);
        // 注意此处传入的是 myHttpServletResponseWrapper, 而不是 response 参数, 这样才能获取页面返回内容
        chain.doFilter(req, myHttpServletResponseWrapper);

        if (myHttpServletResponseWrapper.getStatus() == 0 || myHttpServletResponseWrapper.getStatus() == 200) {
            if (myHttpServletResponseWrapper.getData().length > 0) {
                String data = new String(myHttpServletResponseWrapper.getData());

                // 当然在这之前可以进行订制化处理数据, 然后返回浏览器。 页面装饰框架 sitemesh, 就是在这对 HTML 装饰处理后返回页面的

                // 写到浏览器
                res.getWriter().write(data);
                // 放入缓存, 缓存 7200 秒
                pageCache.put(key, data, 7200);
                return;
            } else { // 没数据则, 缓存清空
                pageCache.put(key, "");
                chain.doFilter(request, response);
            }
        }
    } else { // 没缓存则继续执行
        chain.doFilter(request, response);
    }
} else { // 不缓存页面的处理流程
    if (fullUrl.contains("debug=1")) {
        pageCache.remove(key);
    }
    chain.doFilter(request, response);
}
}

}

web.xml 配置过滤器:

<filter>
    <filter-name>cache</filter-name>
    <filter-class>com.wenniuwuren.CacheFilter</filter-class>
    <init-param>
        <param-name>cachePath</param-name>
        <param-value>/var/cache/project</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>cache</filter-name>
    <url-pattern>*.*</url-pattern>
</filter-mapping>


获取结果如下图: outstr 得到了页面返回的 HTML 代码:

技术分享



版权声明:本文为博主原创文章,未经博主允许不得转载。

获取返回浏览器的内容 —— Java 缓存的使用

标签:memcached   缓存   java   性能优化   

原文地址:http://blog.csdn.net/wenniuwuren/article/details/49357745

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!