這篇文章將為大家詳細(xì)講解有關(guān)SpringCache如何實(shí)現(xiàn)請(qǐng)求級(jí)別緩存,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
創(chuàng)新互聯(lián)公司專注于泰寧網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供泰寧營(yíng)銷型網(wǎng)站建設(shè),泰寧網(wǎng)站制作、泰寧網(wǎng)頁(yè)設(shè)計(jì)、泰寧網(wǎng)站官網(wǎng)定制、微信小程序定制開(kāi)發(fā)服務(wù),打造泰寧網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供泰寧網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。
要將數(shù)據(jù)緩存在一次請(qǐng)求周期內(nèi),那我們先得區(qū)分是什么環(huán)境下的請(qǐng)求,以分析我們?nèi)绾未鎯?chǔ)數(shù)據(jù)。
Web環(huán)境下的有個(gè)絕佳的數(shù)據(jù)存儲(chǔ)位置 HttpServletRequest的Attribute 。調(diào)用setAttribute和getAttribute方法就能輕易地將我們的數(shù)據(jù)用key-value的形式存儲(chǔ)在請(qǐng)求上,而且每次請(qǐng)求都自動(dòng)擁有一個(gè)干凈的Request。想要獲取到HttpServletRequest 也非常簡(jiǎn)單,在web請(qǐng)求中隨時(shí)隨地調(diào)用((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest() 即可。
我司所使用的rpc框架是基于finagle自研的,對(duì)外提供服務(wù)時(shí)使用線程池進(jìn)行處理請(qǐng)求,即對(duì)于一次完整的請(qǐng)求,會(huì)使用同一個(gè)線程進(jìn)行處理。首先想到的辦法還是改動(dòng)這個(gè)rpc框架服務(wù)端,增加一個(gè)可以對(duì)外暴露的、可以key-value存儲(chǔ)的請(qǐng)求上下文。為了能在方便的地方獲取到這個(gè)請(qǐng)求上下文,得將其存儲(chǔ)在ThreadLocal中。
綜合這兩種環(huán)境考慮,我們最好還是實(shí)現(xiàn)一個(gè)統(tǒng)一的方案以減少維護(hù)和開(kāi)發(fā)成本。Spring的RequestContextHolder.getRequestAttributes()其實(shí)也是使用ThreadLocal來(lái)實(shí)現(xiàn)的,那我們可以統(tǒng)一將數(shù)據(jù)存到ThreadLocal<Map<Object,Object>>,自己來(lái)維護(hù)緩存的清理。
存儲(chǔ)位置有了,接下來(lái)實(shí)現(xiàn)SpringCache思路就比較清晰了。
要實(shí)現(xiàn)SpringCache需要一個(gè)CacheManager,接口定義如下
xxxxxxxxxx
public interface CacheManager {
Cache getCache(String name);
Collection<String> getCacheNames();
}
可以看到其實(shí)只需要實(shí)現(xiàn)Cache接口就行了。 在上一篇文章中提到的SimpleCacheManager,它的Cache實(shí)現(xiàn)ConcurrentMapCache內(nèi)部的存儲(chǔ)是依賴ConcurrentMap<Object, Object>。我們的實(shí)現(xiàn)跟它非常類似,最主要的不同是我們需要使用ThreadLocal<Map<Object, Object>> 下面給出幾處關(guān)鍵的實(shí)現(xiàn),其他部分簡(jiǎn)單看下ConcurrentMapCache就能明白。
我們選擇不直接繼承Cache而是AbstractValueAdaptingCache,其被大多數(shù)緩存實(shí)現(xiàn)所繼承,它的作用主要是包裝value值以區(qū)分是沒(méi)有命中緩存還是緩存的null值。
xxxxxxxxxx
private final ThreadLocal<Map<Object, Object>> store = ThreadLocal.withInitial(() -> new HashMap<>(128));
我們的緩存數(shù)據(jù)存儲(chǔ)的地方,ThreadLocal保證緩存只會(huì)存在于這一個(gè)線程中。同時(shí)又因?yàn)橹挥幸粋€(gè)線程能夠訪問(wèn),我們簡(jiǎn)單地使用HashMap即可。
xxxxxxxxxx
public <T> T get(Object key, Callable<T> valueLoader) {
return (T) fromStoreValue(this.store.get().computeIfAbsent(key, r -> {
try {
return toStoreValue(valueLoader.call());
} catch (Throwable ex) {
throw new ValueRetrievalException(key, valueLoader, ex);
}
}));
}
至此我們即將大功告成,只差一個(gè)步驟,ThreadLocal的清理:使用AOP實(shí)現(xiàn)即可。
xxxxxxxxxx
@After("bean(server)")
public void clearThreadCache() {
threadCacheManager.clear();
}
記得將Cache的clear方法通過(guò)我們自定義的CacheManager暴露出來(lái)。同時(shí)也要確保切面能覆蓋每個(gè)請(qǐng)求的結(jié)束。
從以上一個(gè)簡(jiǎn)單的ThreadLocalCacheManager實(shí)現(xiàn),我們對(duì)CacheManager又有了更多的理解。
同時(shí)可能也會(huì)有更多的疑問(wèn)。
再回顧Spring Cache為我們提供的@Cacheable中的sync的注釋,它提到此功能的作用是: 同步化對(duì)被注解方法的調(diào)用,使得多個(gè)線程試圖調(diào)用此方法時(shí),只有一個(gè)線程能夠成功調(diào)用,其他線程直接取這次調(diào)用的返回值。同時(shí)也提到這僅僅只是個(gè)hint,是否真的能成還是要看緩存提供者。
我們找到Spring Cache處理緩存調(diào)用的關(guān)鍵方法org.springframework.cache.interceptor.CacheAspectSupport#execute(org.springframework.cache.interceptor.CacheOperationInvoker, java.lang.reflect.Method, org.springframework.cache.interceptor.CacheAspectSupport.CacheOperationContexts) (spring-context-5.1.5.RELEASE)
經(jīng)過(guò)分析,當(dāng)sync = true 時(shí), 只會(huì)調(diào)用如下代碼
xxxxxxxxxx
return wrapCacheValue(method, cache.get(key, () -> unwrapReturnValue(invokeOperation(invoker))))
即我們上文實(shí)現(xiàn)的T get(Object key, Callable<T> valueLoader) 方法,回頭一看一切都清晰了。 只要我們的this.store.get().computeIfAbsent是同步的,那這個(gè)sync = true就起作用了。 當(dāng)然我們這里使用的HashMap不支持,但是我們?nèi)绻麚Q成ConcurrentMap就能夠?qū)崿F(xiàn)同步化的功能。另外簡(jiǎn)單粗暴地讓方法同步也是可以的(redisCache就是這樣做的)。
當(dāng)sync = false時(shí),會(huì)組合Cache中其他的方法進(jìn)行緩存的處理。邏輯較為簡(jiǎn)單清晰,自行閱讀源碼即可。
異步操作分兩種情況,直接創(chuàng)建線程或者使用線程池。對(duì)于第一種情況我們可以簡(jiǎn)單地使用java.lang.InheritableThreadLocal 來(lái)替代ThreadLocal,創(chuàng)建的子進(jìn)程會(huì)自然而然地共享父進(jìn)程的InheritableThreadLocal;第二種情況就相對(duì)比較復(fù)雜了,建議可以參考 alibaba/transmittable-thread-local ,它實(shí)現(xiàn)了線程池下的ThreadLocal值傳遞功能。
關(guān)于“SpringCache如何實(shí)現(xiàn)請(qǐng)求級(jí)別緩存”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
網(wǎng)頁(yè)標(biāo)題:SpringCache如何實(shí)現(xiàn)請(qǐng)求級(jí)別緩存
瀏覽路徑:http://jinyejixie.com/article38/johhsp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供商城網(wǎng)站、動(dòng)態(tài)網(wǎng)站、建站公司、營(yíng)銷型網(wǎng)站建設(shè)、自適應(yīng)網(wǎng)站、搜索引擎優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)