SpringCache如何實現(xiàn)請求級別緩存

這篇文章將為大家詳細(xì)講解有關(guān)SpringCache如何實現(xiàn)請求級別緩存,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

創(chuàng)新互聯(lián)公司專注于泰寧網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供泰寧營銷型網(wǎng)站建設(shè),泰寧網(wǎng)站制作、泰寧網(wǎng)頁設(shè)計、泰寧網(wǎng)站官網(wǎng)定制、微信小程序定制開發(fā)服務(wù),打造泰寧網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供泰寧網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

方案分析

要將數(shù)據(jù)緩存在一次請求周期內(nèi),那我們先得區(qū)分是什么環(huán)境下的請求,以分析我們?nèi)绾未鎯?shù)據(jù)。

1. Web

Web環(huán)境下的有個絕佳的數(shù)據(jù)存儲位置 HttpServletRequest的Attribute 。調(diào)用setAttribute和getAttribute方法就能輕易地將我們的數(shù)據(jù)用key-value的形式存儲在請求上,而且每次請求都自動擁有一個干凈的Request。想要獲取到HttpServletRequest 也非常簡單,在web請求中隨時隨地調(diào)用((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest() 即可。

2. RPC框架

我司所使用的rpc框架是基于finagle自研的,對外提供服務(wù)時使用線程池進行處理請求,即對于一次完整的請求,會使用同一個線程進行處理。首先想到的辦法還是改動這個rpc框架服務(wù)端,增加一個可以對外暴露的、可以key-value存儲的請求上下文。為了能在方便的地方獲取到這個請求上下文,得將其存儲在ThreadLocal中。

綜合這兩種環(huán)境考慮,我們最好還是實現(xiàn)一個統(tǒng)一的方案以減少維護和開發(fā)成本。Spring的RequestContextHolder.getRequestAttributes()其實也是使用ThreadLocal來實現(xiàn)的,那我們可以統(tǒng)一將數(shù)據(jù)存到ThreadLocal<Map<Object,Object>>,自己來維護緩存的清理。

存儲位置有了,接下來實現(xiàn)SpringCache思路就比較清晰了。

實現(xiàn)SpringCache

要實現(xiàn)SpringCache需要一個CacheManager,接口定義如下

xxxxxxxxxx
public interface CacheManager {
           Cache getCache(String name);
           Collection<String> getCacheNames();
}

可以看到其實只需要實現(xiàn)Cache接口就行了。 在上一篇文章中提到的SimpleCacheManager,它的Cache實現(xiàn)ConcurrentMapCache內(nèi)部的存儲是依賴ConcurrentMap<Object, Object>。我們的實現(xiàn)跟它非常類似,最主要的不同是我們需要使用ThreadLocal<Map<Object, Object>> 下面給出幾處關(guān)鍵的實現(xiàn),其他部分簡單看下ConcurrentMapCache就能明白。

1 extends  

我們選擇不直接繼承Cache而是AbstractValueAdaptingCache,其被大多數(shù)緩存實現(xiàn)所繼承,它的作用主要是包裝value值以區(qū)分是沒有命中緩存還是緩存的null值。

2 store

xxxxxxxxxx
private final ThreadLocal<Map<Object, Object>> store = ThreadLocal.withInitial(() -> new HashMap<>(128));

我們的緩存數(shù)據(jù)存儲的地方,ThreadLocal保證緩存只會存在于這一個線程中。同時又因為只有一個線程能夠訪問,我們簡單地使用HashMap即可。

3 get

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);
        }
     }));
 }

至此我們即將大功告成,只差一個步驟,ThreadLocal的清理:使用AOP實現(xiàn)即可。

xxxxxxxxxx
    @After("bean(server)")
    public void clearThreadCache() {
        threadCacheManager.clear();
    }

記得將Cache的clear方法通過我們自定義的CacheManager暴露出來。同時也要確保切面能覆蓋每個請求的結(jié)束。

總結(jié)與擴展

從以上一個簡單的ThreadLocalCacheManager實現(xiàn),我們對CacheManager又有了更多的理解。

同時可能也會有更多的疑問。

1. 我們實現(xiàn)的這些方法,從方法名和邏輯上看起來都很簡單,那他們是如何配合使用的?跟@Cacheable上的sync又有什么關(guān)系呢?

再回顧Spring Cache為我們提供的@Cacheable中的sync的注釋,它提到此功能的作用是: 同步化對被注解方法的調(diào)用,使得多個線程試圖調(diào)用此方法時,只有一個線程能夠成功調(diào)用,其他線程直接取這次調(diào)用的返回值。同時也提到這僅僅只是個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)過分析,當(dāng)sync = true 時, 只會調(diào)用如下代碼

xxxxxxxxxx
return wrapCacheValue(method, cache.get(key, () -> unwrapReturnValue(invokeOperation(invoker))))

即我們上文實現(xiàn)的T get(Object key, Callable<T> valueLoader) 方法,回頭一看一切都清晰了。 只要我們的this.store.get().computeIfAbsent是同步的,那這個sync = true就起作用了。 當(dāng)然我們這里使用的HashMap不支持,但是我們?nèi)绻麚Q成ConcurrentMap就能夠?qū)崿F(xiàn)同步化的功能。另外簡單粗暴地讓方法同步也是可以的(redisCache就是這樣做的)。

當(dāng)sync = false時,會組合Cache中其他的方法進行緩存的處理。邏輯較為簡單清晰,自行閱讀源碼即可。

2. 用ThreadLocal嚴(yán)格來說實現(xiàn)的只是線程內(nèi)的緩存,萬一一次請求中有異步操作怎么辦?

異步操作分兩種情況,直接創(chuàng)建線程或者使用線程池。對于第一種情況我們可以簡單地使用java.lang.InheritableThreadLocal 來替代ThreadLocal,創(chuàng)建的子進程會自然而然地共享父進程的InheritableThreadLocal;第二種情況就相對比較復(fù)雜了,建議可以參考 alibaba/transmittable-thread-local ,它實現(xiàn)了線程池下的ThreadLocal值傳遞功能。

關(guān)于“SpringCache如何實現(xiàn)請求級別緩存”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

網(wǎng)頁標(biāo)題:SpringCache如何實現(xiàn)請求級別緩存
瀏覽路徑:http://muchs.cn/article38/johhsp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供商城網(wǎng)站、動態(tài)網(wǎng)站、建站公司、營銷型網(wǎng)站建設(shè)自適應(yīng)網(wǎng)站、搜索引擎優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

微信小程序開發(fā)