變通實現微服務的per request以提高IO效率(二)遺留一個問題,如何正確的釋放存儲在ThreadLocal中的緩存,最理由就是在我們請求的方法執行完成後去清除緩存。
由於我的項目是基於dubbo的,所以可以利用dubbo提供的Filter機制去完成這件事情,可以看下filter的地位:
最終的效果:
創建一個類讓其實現Filter接口,就一個方法invoke,這個invoke方法的功能類似於AOP的Around方法,我們想清除緩存就有地方操作了,只需要在return的前面,invoker.invoke方法後面添加相應的清除邏輯即可達到目的。由於緩存是線程獨有的,所以直接清空就可以。
由於Filter加載機制問題,在Filter中使用Spring的注解是有點問題的,暫時是通過手動獲取Bean的方式來加載cacheManager,後面在看dubbo的filter加載機制時會有簡單提到。大家如果有其它好的方案可以告訴我
@Activate public class ThreadLocalCacheFilter implements Filter { private Logger logger = LoggerFactory.getLogger(getClass().getName()); @Autowired private CacheManager cacheManager; private void clearCache(){ if(null==cacheManager){ ApplicationContext appCtx = ApplicationContextUtils.getApplicationContext(); cacheManager= appCtx.getBean(ThreadLocalCacheManager.class); } Collection<String> cacheNames= this.cacheManager.getCacheNames(); if(null!=cacheNames) { for(String cacheName :cacheNames) { this.cacheManager.getCache(cacheName).clear(); } } } @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { Result result=invoker.invoke(invocation); this.logger.info("release cache start"); this.clearCache(); this.logger.info("release cache end"); return result; } }
@Active注解
要想激活filter,我們需要在創建的自定義filter類上加載@Active注解,看下它的相關參數,也可以不配置
編寫的擴展filter,dubbo需要加載成功後才能使用,dubbo總共從resource下面的三個目錄中加載filter
創建純文件文件com.alibaba.dubbo.rpc.Filter放入對應的目錄,然後寫入需要使用的filter信息
threadLocalCacheFilter=com.filter.ThreadLocalCacheFilter
在dubbo配置文件中增加如下內容:
<dubbo:provider filter="threadLocalCacheFilter" />
dubbo有這樣一個類ProtocolFilterWrapper,它負責加載項目中所有的filter,並負責鏈式調用。
想學習設計模式的可以看看這個類是如何使用職責鏈模式的
這裡只看一個方法就可以了:
注意變量next,當前方法在執行invoke方法時,將調用傳遞到了next。這裡應該會有最後一個終結器來處理實際方法的執行。
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.size() > 0) { for (int i = filters.size() - 1; i >= 0; i --) { final Filter filter = filters.get(i); final Invoker<T> next = last; last = new Invoker<T>() { public Class<T> getInterface() { return invoker.getInterface(); } public URL getUrl() { return invoker.getUrl(); } public boolean isAvailable() { return invoker.isAvailable(); } public Result invoke(Invocation invocation) throws RpcException { return filter.invoke(next, invocation); } public void destroy() { invoker.destroy(); } @Override public String toString() { return invoker.toString(); } }; } } return last; }
結過三篇筆記,從最初的Context問題,到緩存的釋放,基本可以非常方便的使用請求級的緩存了。這裡需要注意的是需要明確哪些方案是適合做請求級緩存的。比如查詢用戶,有些操作中先插入用戶然後再查詢,如果查詢的是被標記了請求級緩存的方法就會有問題。