緩存是最直接有效提升系統(tǒng)性能的手段之一。個(gè)人認(rèn)為用好用對(duì)緩存是優(yōu)秀程序員的必備基本素質(zhì)。本文結(jié)合實(shí)際開發(fā)經(jīng)驗(yàn),從簡(jiǎn)單概念原理和代碼入手,一步一步搭建一個(gè)簡(jiǎn)單的二級(jí)緩存系統(tǒng)。
一、通用緩存接口
1、緩存基礎(chǔ)算法
-
FIFO(First In First Out),先進(jìn)先出,和OS里的FIFO思路相同,如果一個(gè)數(shù)據(jù)最先進(jìn)入緩存中,當(dāng)緩存滿的時(shí)候,應(yīng)當(dāng)把最先進(jìn)入緩存的數(shù)據(jù)給移除掉。
-
LFU(Least Frequently Used),最不經(jīng)常使用,如果一個(gè)數(shù)據(jù)在最近一段時(shí)間內(nèi)使用次數(shù)很少,那么在將來(lái)一段時(shí)間內(nèi)被使用的可能性也很小。
-
LRU(Least Recently Used),最近最少使用,如果一個(gè)數(shù)據(jù)在最近一段時(shí)間沒有被訪問到,那么在將來(lái)它被訪問的可能性也很小。也就是說(shuō),當(dāng)限定的空間已存滿數(shù)據(jù)時(shí),應(yīng)當(dāng)把最久沒有被訪問到的數(shù)據(jù)移除。
2、接口定義
簡(jiǎn)單定義緩存接口,大致可以抽象如下:
package com.power.demo.cache.contract;
import?java.util.function.Function;
/**
?* 緩存提供者接口
?**/
public?interface?CacheProviderService {
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????<T?extends?Object> T?get(String?key);
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對(duì)象 可為空
?????**/
????<T?extends?Object> T?get(String?key,?Function<String, T>?function);
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對(duì)象 可為空
?????* @param funcParm function函數(shù)的調(diào)用參數(shù)
?????**/
????<T?extends?Object, M?extends?Object> T?get(String?key,?Function<M, T>?function,?M?funcParm);
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對(duì)象 可為空
?????* @param expireTime 過期時(shí)間(單位:毫秒) 可為空
?????**/
????<T?extends?Object> T?get(String?key,?Function<String, T>?function,?Long?expireTime);
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對(duì)象 可為空
?????* @param funcParm function函數(shù)的調(diào)用參數(shù)
?????* @param expireTime 過期時(shí)間(單位:毫秒) 可為空
?????**/
????<T?extends?Object, M?extends?Object> T?get(String?key,?Function<M, T>?function,?M?funcParm,?Long?expireTime);
????/**
?????* 設(shè)置緩存鍵值
?????*
?????* @param key 緩存鍵 不可為空
?????* @param obj 緩存值 不可為空
?????**/
????<T?extends?Object>?void?set(String?key, T obj);
????/**
?????* 設(shè)置緩存鍵值
?????*
?????* @param key 緩存鍵 不可為空
?????* @param obj 緩存值 不可為空
?????* @param expireTime 過期時(shí)間(單位:毫秒) 可為空
?????**/
????<T?extends?Object>?void?set(String?key, T obj, Long expireTime);
????/**
?????* 移除緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????void?remove(String?key);
????/**
?????* 是否存在緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????boolean?contains(String?key);
}
注意,這里列出的只是常見緩存功能接口,一些在特殊場(chǎng)景下用到的統(tǒng)計(jì)類的接口、分布式鎖、自增(減)等功能不在討論范圍之內(nèi)。
Get相關(guān)方法,注意多個(gè)參數(shù)的情況,緩存接口里面?zhèn)魅说腇unction,這是Java8提供的函數(shù)式接口,雖然支持的入?yún)€(gè)數(shù)有限(這里你會(huì)非常懷念.NET下的Func委托),但是僅對(duì)Java這個(gè)語(yǔ)言來(lái)說(shuō),這真是一個(gè)重大的進(jìn)步^_^。
接口定義好了,下面就要實(shí)現(xiàn)緩存提供者程序了。按照存儲(chǔ)類型的不同,本文簡(jiǎn)單實(shí)現(xiàn)最常用的兩種緩存提供者:本地緩存和分布式緩存。
二、本地緩存
本地緩存,也就是JVM級(jí)別的緩存(本地緩存可以認(rèn)為是直接在進(jìn)程內(nèi)通信調(diào)用,而分布式緩存則需要通過網(wǎng)絡(luò)進(jìn)行跨進(jìn)程通信調(diào)用),一般有很多種實(shí)現(xiàn)方式,比如直接使用Hashtable、ConcurrentHashMap等天生線程安全的集合作為緩存容器,或者使用一些成熟的開源組件,如EhCache、Guava Cache等。本文選擇上手簡(jiǎn)單的Guava緩存。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-534105.html
1、什么是Guava
Guava,簡(jiǎn)單來(lái)說(shuō)就是一個(gè)開發(fā)類庫(kù),且是一個(gè)非常豐富強(qiáng)大的開發(fā)工具包,號(hào)稱可以讓使用Java語(yǔ)言更令人愉悅,主要包括基本工具類庫(kù)和接口、緩存、發(fā)布訂閱風(fēng)格的事件總線等。在實(shí)際開發(fā)中,我用的最多的是集合、緩存和常用類型幫助類,很多人都對(duì)這個(gè)類庫(kù)稱贊有加。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-534105.html
2、添加依賴
<dependency>
????<groupId>com.google.guava</groupId>
????<artifactId>guava</artifactId>
</dependency>
3、實(shí)現(xiàn)接口
/*
?* 本地緩存提供者服務(wù) (Guava Cache)
?* */
@Configuration
@ComponentScan(basePackages = AppConst.BASE_PACKAGE_NAME)
@Qualifier("localCacheService")
public?class?LocalCacheProviderImpl?implements?CacheProviderService {
????private?static?Map<String, Cache<String,?Object>> _cacheMap = Maps.newConcurrentMap();
????static?{
????????Cache<String,?Object> cacheContainer = CacheBuilder.newBuilder()
????????????????.maximumSize(AppConst.CACHE_MAXIMUM_SIZE)
????????????????.expireAfterWrite(AppConst.CACHE_MINUTE, TimeUnit.MILLISECONDS)//最后一次寫入后的一段時(shí)間移出
????????????????//.expireAfterAccess(AppConst.CACHE_MINUTE, TimeUnit.MILLISECONDS) //最后一次訪問后的一段時(shí)間移出
????????????????.recordStats()//開啟統(tǒng)計(jì)功能
????????????????.build();
????????_cacheMap.put(String.valueOf(AppConst.CACHE_MINUTE), cacheContainer);
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????public?<T?extends?Object> T?get(String?key) {
????????T obj =?get(key,?null,?null, AppConst.CACHE_MINUTE);
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對(duì)象 可為空
?????**/
????public?<T?extends?Object> T?get(String?key,?Function<String, T>?function)?{
????????T obj =?get(key,?function,?key,?AppConst.CACHE_MINUTE);
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對(duì)象 可為空
?????* @param funcParm function函數(shù)的調(diào)用參數(shù)
?????**/
????public?<T?extends?Object, M?extends?Object> T?get(String?key,?Function<M, T>?function,?M?funcParm)?{
????????T obj =?get(key,?function,?funcParm,?AppConst.CACHE_MINUTE);
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對(duì)象 可為空
?????* @param expireTime 過期時(shí)間(單位:毫秒) 可為空
?????**/
????public?<T?extends?Object> T?get(String?key,?Function<String, T>?function,?Long?expireTime)?{
????????T obj =?get(key,?function,?key,?expireTime);
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對(duì)象 可為空
?????* @param funcParm function函數(shù)的調(diào)用參數(shù)
?????* @param expireTime 過期時(shí)間(單位:毫秒) 可為空
?????**/
????public?<T?extends?Object, M?extends?Object> T?get(String?key,?Function<M, T>?function,?M?funcParm,?Long?expireTime)?{
????????T obj =?null;
????????if?(StringUtils.isEmpty(key) ==?true) {
????????????return?obj;
????????}
????????expireTime = getExpireTime(expireTime);
????????Cache<String,?Object> cacheContainer = getCacheContainer(expireTime);
????????try?{
????????????if?(function?==?null)?{
????????????????obj = (T) cacheContainer.getIfPresent(key);
????????????}?else?{
????????????????final Long cachedTime = expireTime;
????????????????obj = (T) cacheContainer.get(key, () -> {
????????????????????T retObj =?
到了這里,關(guān)于Spring Boot 緩存應(yīng)用實(shí)踐的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!