首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> JSP 程式設計 >> 關於JSP

JSP頁面輸出捕獲緩存技術:Filter包裝Response實現靜態化加速

     編輯:關於JSP

高頻訪問、低頻更新的頁面(如論壇總版、首頁聚合),每次請求執行JSP渲染浪費CPU。利用Filter包裝HttpServletResponse捕獲out輸出,首次生成靜態HTML文件/內存字符串,後續請求直接返回靜態內容,實現「偽靜態化」加速。本文演示基於CharArrayWriter的輸出捕獲全鏈路。

核心原理

  1. Filter鏈doFilter()前,創建HttpServletResponseWrapper子類,重寫getWriter()返回PrintWriter(CharArrayWriter)。
  2. 調用chain.doFilter(req, wrapper),JSP正常執行,所有out.print()寫入內存CharArrayWriter。
  3. 鏈返回後,wrapper.toString()獲取完整HTML,寫入文件或緩存對象。
  4. 後續請求直接讀取靜態文件/緩存返回,繞過JSP引擎。
  5. 數據更新時(發帖、回覆),觸發緩存失效/重建。

完整代碼實現

1. Filter類(FileCaptureFilter)

package com.junjing.filter;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class FileCaptureFilter implements Filter {
    private String realPathPrefix;

    public void init(FilterConfig config) throws ServletException {
        realPathPrefix = config.getServletContext().getRealPath("/");
    }

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

        // 目標靜態文件路徑
        String staticFile = realPathPrefix + "forum/latest.html";

        // 包裝Response捕獲輸出
        CaptureResponseWrapper wrapper = new CaptureResponseWrapper(response);
        chain.doFilter(request, wrapper); // JSP執行,輸出被捕獲

        // 獲取生成的HTML
        String html = wrapper.getCapturedHtml();

        // 寫入靜態文件(生產建議:異步寫入、文件鎖、臨時文件重命名)
        writeStaticFile(staticFile, html);

        // 直接輸出給瀏覽器(首次請求)
        response.getWriter().print(html);
    }

    private void writeStaticFile(String path, String content) {
        try (FileWriter fw = new FileWriter(path)) {
            fw.write(content);
        } catch (IOException e) {
            e.printStackTrace(); // 生產接入日志框架
        }
    }

    public void destroy() {}
}

2. Response包裝類(CaptureResponseWrapper)

package com.junjing.filter;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class CaptureResponseWrapper extends HttpServletResponseWrapper {
    private final CharArrayWriter buffer = new CharArrayWriter();
    private PrintWriter writer;

    public CaptureResponseWrapper(HttpServletResponse response) {
        super(response);
    }

    @Override
    public PrintWriter getWriter() {
        if (writer == null) {
            writer = new PrintWriter(buffer);
        }
        return writer;
    }

    @Override
    public ServletOutputStream getOutputStream() {
        throw new UnsupportedOperationException("此Filter僅支持字符輸出捕獲");
    }

    public String getCapturedHtml() {
        if (writer != null) writer.flush();
        return buffer.toString();
    }
}

3. web.xml配置

<filter>
    <filter-name>FileCaptureFilter</filter-name>
    <filter-class>com.junjing.filter.FileCaptureFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>FileCaptureFilter</filter-name>
    <url-pattern>/latest.jsp</url-pattern>
</filter-mapping>

進階優化建議

  • 緩存策略:首選內存緩存(Caffeine/Guava Cache,Key=URL,Value=HTML字符串,TTL+主動失效),文件落地作為持久化備份/跨實例共享。
  • 併發安全:緩存重建用Cache.get(key, Callable)單飛機制,避免緩存擊穿導致大量線程同時執行JSP。
  • 動態片段:頁面含用戶個性化區塊(如右上角用戶名),採用ESI (Edge Side Includes)或AJAX異步加載片段,主頁緩存公共部分。
  • 更新觸發:業務Service層發布事件(SpringApplicationEvent、Guava EventBus),緩存監聽器精準失效相關Key,避免全站緩存清空。
  • 現代替代:Nginxfastcgi_cache/proxy_cache、Varnish、CDN邊緣緩存,將緩存下沉到基礎設施層,應用層無感。

適用性判斷

特徵適用不適用
訪問頻率高 (QPS > 100)低
更新頻率低 (分鐘/小時級)高 (秒級/實時)
個性化程度低/可片段化高度個性化(每用戶不同)
數據一致性要求允許秒級延遲強一致性(金融交易)

「捕獲輸出→靜態化→直接服務」是經典JSP時代的高性能模式,現代架構雖下沉到Nginx/CDN,但理解其原理對排查緩存不生效、內容不一致等問題極有幫助。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved