情景:
在一次服務(wù)改造過程中杆烁,將一些上報性質(zhì)的業(yè)務(wù)與主業(yè)務(wù)進行剝離出來轰驳,中間需要對數(shù)據(jù)字段進行填充惠桃,會產(chǎn)生很多次的RPC塞祈,或者其他的IO操作,為了避免頻繁的RPC或者IO操作循签,從而提升整個業(yè)務(wù)的處理耗時轻掩,所以對其中的大部分數(shù)據(jù)進行緩存
方案一:
首先第一跳出來的本地緩存框架是使用guava,因為在內(nèi)部其他同事負責(zé)的模塊里面有使用這個內(nèi)存框架的業(yè)務(wù)懦底,所以上手起來比較容易唇牧,出問題起來也比較好解決罕扎;so,最初方案選擇就是基于guava去實現(xiàn)了這部分功能丐重,并且也上線了腔召,具體用法這里不做贅述,畢竟網(wǎng)上對guava的介紹還是很多扮惦,可以自行百度臀蛛;
上線后出現(xiàn)的問題:
問題1: 機器cpu使用率不高,但是load負載呈周期性變化崖蜜,每80-90分鐘就會出現(xiàn)一個突刺浊仆;(懷疑可能與緩存框架有關(guān),查詢其他的指標(biāo)例如磁盤io豫领,線程數(shù)過多等都在正常值)
問題2:對guava的一些策略使用的不夠正確抡柿,最開始使用的expireAfterWrite,導(dǎo)致刷新的時候可能會阻塞等恐;當(dāng)使用refreshAfterWrite策略時洲劣,沒有配置expireAfterWrite,就會產(chǎn)生過期的key不會被回收课蔬,因為refresh是基于get請求來刷新對應(yīng)key囱稽,同時因為加載的時候走的是同步加載會阻塞當(dāng)前的進程,影響整體性能二跋;
以上問題給予guava緩存可以通過一些復(fù)雜的改造方案去實現(xiàn)它的功能战惊,解決以上的問題,由于同事的推薦建議考慮學(xué)習(xí)下caffeine扎即,也是基于guava cache做的样傍,so,我去了解并且學(xué)習(xí)了下铺遂;
方案二:
Caffeine是基于JAVA 1.8 Version的高性能緩存庫。Caffeine提供的內(nèi)存緩存使用參考Google guava的API茎刚。Caffeine是基于Google Guava Cache設(shè)計經(jīng)驗上改進的成果襟锐;
看上以上這段文字以后,我就喜歡這個框架了膛锭,因為本人比較熟悉java1.8粮坞;
查詢github上的一些文檔以及官方性能對比;
引自文章鏈接:http://www.reibang.com/p/3434991ad075
可以看到Caffeine的性能是最高的初狰,并且相關(guān)的文檔也比較成熟莫杈;所以果斷進行改造;
使用方式有很多種奢入,但是我查詢了下缺少一些異步加載demo筝闹,所以我這樓闡述下異步加載的方式:
構(gòu)造cache
private ExecutorService commonExecutorService;
AsyncLoadingCache<String, Optional<String>> ramCache = Caffeine.newBuilder().executor(commonExecutorService).expireAfterAccess(timeOut, TimeUnit.SECONDS)
.refreshAfterWrite(180, TimeUnit.SECONDS).initialCapacity(16).maximumSize(500)
.removalListener((com.github.benmanes.caffeine.cache.RemovalListener<String, Optional<String>>) (key, value, cause) -> log.info("key:[{}],value:[{}]被移除了,原因:{}", key, value,cause))
.buildAsync(new AsyncCacheLoader<String, Optional<String>>() {
@Override
public @NonNull CompletableFuture<Optional<String>> asyncLoad(@NonNull String key, @NonNull Executor executor) {
//記載緩存的業(yè)務(wù)方法
},executor);
}
});
//以上是構(gòu)造了一個異步加載的本地緩存,傳入了自定義的線程池,設(shè)置了兩種策略关顷,一種是基于最后一次訪問的時間過期策略糊秆,一種是基于時間的刷新策略,
初始大小為16议双,最大為500痘番,并設(shè)置了監(jiān)聽器,打印相關(guān)日志平痰,設(shè)置異步加載策略汞舱,里面使用傳入的線程池進行加載
注意:本地緩存里如果需要放null值的話,必須用optional宗雇,否則會報錯昂芜;
其余使用方式與guava cache一致