首先,緩存主頁的目的並不一定是提高性能,而是減少數據庫訪問壓力,有效推遲數據庫I/O瓶頸的到來。實現主頁緩存的方法有很多,但是鑒於項目中使用了Redis對數據庫讀寫做了緩存,因此把順便也就把主頁也緩存了吧。
實現思路
編寫一個過濾器,在過濾器中攔截對主頁的訪問請求。此時向Redis服務器查詢主頁html的緩存,如果有則直接返回給客戶端,如果沒有,則在過濾器中截獲JSP的渲染結果,放到Redis緩存中,以供下次使用。我們設定緩存過期時間為10分鐘。
實現
實現需要注意的地方有兩點:
如何在Servlet過慮器中使用Spring容器
如何截獲JSP渲染結果
public class ResponseWrapper extends HttpServletResponseWrapper {
private PrintWriter cachedWriter;
private CharArrayWriter bufferedWriter;
public ResponseWrapper(HttpServletResponse response) {
super(response);
// 這個是我們保存返回結果的地方
bufferedWriter = new CharArrayWriter();
// 這個是包裝PrintWriter的,讓所有結果通過這個PrintWriter寫入到bufferedWriter中
cachedWriter = new PrintWriter(bufferedWriter);
}
@Override
public PrintWriter getWriter() {
return cachedWriter;
}
/**
* 獲取原始的HTML頁面內容。
*
* @return
*/
public String getResult() {
return bufferedWriter.toString();
}
}
然後在過濾器中使用該類:
public class CacheFilter implements Filter, ApplicationContextAware {
private static final Logger log = LoggerFactory.getLogger(CacheFilter.class);
private static ApplicationContext ctx;
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) servletResponse;
HttpServletRequest req = (HttpServletRequest) servletRequest;
// 如果不是訪問主頁,放行
if (false == req.getRequestURI().equals("/")) {
filterChain.doFilter(servletRequest, resp);
return;
}
// 訪問的是主頁
// 從緩存中得到主頁html
String html = getHtmlFromCache();
if (null == html) {
// 緩存中沒有
// 截取生成的html並放入緩存
log.info("緩存不存在,生成緩存");
ResponseWrapper wrapper = new ResponseWrapper(resp);
// ***** 以上代碼在請求被處理之前執行 *****
filterChain.doFilter(servletRequest, wrapper);
// ***** 以下代碼在請求被處理後前執行 *****
// 放入緩存
html = wrapper.getResult();
putIntoCache(html);
}
// 返回響應
resp.setContentType("text/html; charset=utf-8");
resp.getWriter().print(html);
}
@Override
public void destroy() {
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
private String getHtmlFromCache() {
StringRedisTemplate redis = (StringRedisTemplate)ctx.getBean("redisTemplate");
return redis.opsForValue().get("home");
}
private void putIntoCache(String html) {
StringRedisTemplate redis = (StringRedisTemplate)ctx.getBean("redisTemplate");
redis.opsForValue().set("home", html, TimeUnit.MINUTES.toSeconds(10)); // 10分鐘
}
}
按照這個邏輯,當客戶的GET請求為/時,CacheFilter會首先向Redis發起請求獲取主頁的html代碼,如果成功,則直接返回給客戶端,失敗,則通過剛剛寫好的ResponseWrapper截獲主頁JSP的渲染結果,放入Redis,並設置過期時間為10分鐘。這樣下次請求時就可以直接從緩存中讀取,而不需要經過 Controller -> Service -> Dao -> Database這麼費事的流程了。
Ubuntu 14.04下Redis安裝及簡單測試 http://www.linuxidc.com/Linux/2014-05/101544.htm
Redis集群明細文檔 http://www.linuxidc.com/Linux/2013-09/90118.htm
Ubuntu 12.10下安裝Redis(圖文詳解)+ Jedis連接Redis http://www.linuxidc.com/Linux/2013-06/85816.htm
Redis系列-安裝部署維護篇 http://www.linuxidc.com/Linux/2012-12/75627.htm
CentOS 6.3安裝Redis http://www.linuxidc.com/Linux/2012-12/75314.htm
Redis安裝部署學習筆記 http://www.linuxidc.com/Linux/2014-07/104306.htm
Redis配置文件redis.conf 詳解 http://www.linuxidc.com/Linux/2013-11/92524.htm
Redis 的詳細介紹:請點這裡
Redis 的下載地址:請點這裡