變通實現微服務的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問題,到緩存的釋放,基本可以非常方便的使用請求級的緩存了。這裡需要注意的是需要明確哪些方案是適合做請求級緩存的。比如查詢用戶,有些操作中先插入用戶然後再查詢,如果查詢的是被標記了請求級緩存的方法就會有問題。