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

微服務請求級緩存落地(三):基於 Dubbo Filter 實現 ThreadLocal 緩存自動釋放

     編輯:Java 綜合教學

問題背景

前文實現了基於 ThreadLocal 的請求級緩存,遺留關鍵問題:請求結束後如何準確清理緩存,防止內存洩漏與數據污染。最佳時機是在業務方法執行完成後統一釋放。

解決方案:Dubbo Filter 攔截器鏈

項目基於 Dubbo 框架,利用其 Filter 機制(類似 AOP 環繞通知)在服務提供方攔截調用鏈,於 invoker.invoke() 返回前清理 ThreadLocal 緩存。

Filter 核心代碼

@Activate
public class ThreadLocalCacheFilter implements Filter {
    private static final Logger logger = LoggerFactory.getLogger(ThreadLocalCacheFilter.class);

    @Autowired
    private CacheManager cacheManager;

    private void clearCache() {
        // 因 Filter 加載機制限制,Spring 注解注入可能失效,採用手動獲取 Bean
        if (cacheManager == null) {
            ApplicationContext ctx = ApplicationContextUtils.getApplicationContext();
            cacheManager = ctx.getBean(ThreadLocalCacheManager.class);
        }
        Collection<String> cacheNames = cacheManager.getCacheNames();
        if (cacheNames != null) {
            for (String name : cacheNames) {
                cacheManager.getCache(name).clear();
            }
        }
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        Result result = invoker.invoke(invocation); // 執行業務邏輯
        logger.info("release cache start");
        clearCache(); // 業務結束,清理緩存
        logger.info("release cache end");
        return result;
    }
}

@Activate 註解參數說明

  • group:指定生效端(provider/consumer)
  • value:Filter 名稱,用於配置文件引用
  • before/after/order:控制 Filter 執行順序

Filter 註冊與啟用

  1. 在 resources/META-INF/dubbo/(或 services/、dubbo/internal/)創建文件 com.alibaba.dubbo.rpc.Filter
  2. 文件內容:threadLocalCacheFilter=com.filter.ThreadLocalCacheFilter
  3. Dubbo 提供方配置啟用:
    <dubbo:provider filter="threadLocalCacheFilter" />

原理深度解析:ProtocolFilterWrapper 建立責任鏈

Dubbo 內部 ProtocolFilterWrapper.buildInvokerChain() 負責加載所有 Filter,按排序構建調用鏈:

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    Invoker<T> last = invoker;
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class)
            .getActivateExtension(invoker.getUrl(), key, group);
    if (!filters.isEmpty()) {
        for (int i = filters.size() - 1; i >= 0; i--) {
            final Filter filter = filters.get(i);
            final Invoker<T> next = last;
            last = new Invoker<T>() {
                @Override public Result invoke(Invocation invocation) throws RpcException {
                    return filter.invoke(next, invocation); // 責任鏈傳遞
                }
                // ... 其他方法委託給 last
            };
        }
    }
    return last;
}

每個 Filter 的 invoke() 可決定是否調用 next.invoke() 繼續鏈路,最終到達真實業務邏輯。我們的清理邏輯置於 next.invoke() 之後,確保業務完成後執行。

總結與避坑指南

  • 三篇文章串聯:Context 傳遞 → 緩存隔離 → 生命週期管理,構建完整請求級緩存方案;
  • 適用場景:單次請求內重複查詢(如用戶信息、配置項),避免多次 RPC/DB 訪問;
  • 禁忌:寫操作後緊接讀操作、事務邊界內、數據強一致性要求場景不可使用請求級緩存,否則讀到髒數據;
  • Filter 中 Spring 注入失效屬於 Dubbo 擴展點加載時機早於 Spring 容器初始化,採用 ApplicationContextUtils 手動獲取 Bean 為通用變通方案。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved