(Reference-based Eviction)強(strong)、軟(soft)、弱(weak)、虛(phantom)引用-參考: 經過使用弱引用的鍵、或弱引用的值、或軟引用的值GuavaCache能夠把緩存設置爲容許垃圾回收:java
Guava cache 是利用CacheBuilder類用builder模式構造出兩種不一樣的cache加載方式CacheLoader,Callable,共同邏輯都是根據key是加載value。不一樣的地方在於CacheLoader的定義比較寬泛,是針對整個cache定義的,能夠認爲是統一的根據key值load value的方法,而Callable的方式較爲靈活,容許你在get的時候指定load方法。看如下代碼:git
Cache<String,Object> cache = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.SECONDS).maximumSize(500).build();
cache.get("key", new Callable<Object>() { //Callable 加載
@Override
public Object call() throws Exception {
return "value";
}
});
LoadingCache<String, Object> loadingCache = CacheBuilder.newBuilder()
.expireAfterAccess(30, TimeUnit.SECONDS).maximumSize(5)
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) throws Exception {
return "value";
}
});
複製代碼
guava作cache時候數據的移除方式,在guava中數據的移除分爲被動移除和主動移除兩種。github
- 被動移除數據的方式,guava默認提供了三種方式:
定義的方式通常爲 CacheBuilder.maximumSize(long),還有一種一種能夠算權重的方法,我的認爲實際使用中不太用到。就這個經常使用的來看有幾個注意點,web
- 主動移除數據方式,主動移除有三種方法:
若是須要在移除數據的時候有所動做還能夠定義Removal Listener,可是有點須要注意的是默認Removal Listener中的行爲是和移除動做同步執行的,若是須要改爲異步形式,能夠考慮使用RemovalListeners.asynchronous(RemovalListener, Executor)redis
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
複製代碼
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @ClassName: GuavaAbstractLoadingCache
* @author LiJing
* @date 2019/07/02 11:09
*
*/
public abstract class GuavaAbstractLoadingCache <K, V> {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
//用於初始化cache的參數及其缺省值
private int maximumSize = 1000; //最大緩存條數,子類在構造方法中調用setMaximumSize(int size)來更改
private int expireAfterWriteDuration = 60; //數據存在時長,子類在構造方法中調用setExpireAfterWriteDuration(int duration)來更改
private TimeUnit timeUnit = TimeUnit.MINUTES; //時間單位(分鐘)
private Date resetTime; //Cache初始化或被重置的時間
private long highestSize=0; //歷史最高記錄數
private Date highestTime; //創造歷史記錄的時間
private LoadingCache<K, V> cache;
/**
* 經過調用getCache().get(key)來獲取數據
* @return cache
*/
public LoadingCache<K, V> getCache() {
if(cache == null){ //使用雙重校驗鎖保證只有一個cache實例
synchronized (this) {
if(cache == null){
cache = CacheBuilder.newBuilder().maximumSize(maximumSize) //緩存數據的最大條目,也可使用.maximumWeight(weight)代替
.expireAfterWrite(expireAfterWriteDuration, timeUnit) //數據被建立多久後被移除
.recordStats() //啓用統計
.build(new CacheLoader<K, V>() {
@Override
public V load(K key) throws Exception {
return fetchData(key);
}
});
this.resetTime = new Date();
this.highestTime = new Date();
logger.debug("本地緩存{}初始化成功", this.getClass().getSimpleName());
}
}
}
return cache;
}
/**
* 根據key從數據庫或其餘數據源中獲取一個value,並被自動保存到緩存中。
* @param key
* @return value,連同key一塊兒被加載到緩存中的。
*/
protected abstract V fetchData(K key) throws Exception;
/**
* 從緩存中獲取數據(第一次自動調用fetchData從外部獲取數據),並處理異常
* @param key
* @return Value
* @throws ExecutionException
*/
protected V getValue(K key) throws ExecutionException {
V result = getCache().get(key);
if(getCache().size() > highestSize){
highestSize = getCache().size();
highestTime = new Date();
}
return result;
}
public long getHighestSize() {
return highestSize;
}
public Date getHighestTime() {
return highestTime;
}
public Date getResetTime() {
return resetTime;
}
public void setResetTime(Date resetTime) {
this.resetTime = resetTime;
}
public int getMaximumSize() {
return maximumSize;
}
public int getExpireAfterWriteDuration() {
return expireAfterWriteDuration;
}
public TimeUnit getTimeUnit() {
return timeUnit;
}
/**
* 設置最大緩存條數
* @param maximumSize
*/
public void setMaximumSize(int maximumSize) {
this.maximumSize = maximumSize;
}
/**
* 設置數據存在時長(分鐘)
* @param expireAfterWriteDuration
*/
public void setExpireAfterWriteDuration(int expireAfterWriteDuration) {
this.expireAfterWriteDuration = expireAfterWriteDuration;
}
}
複製代碼
public interface ILocalCache <K, V> {
/**
* 從緩存中獲取數據
* @param key
* @return value
*/
public V get(K key);
}
複製代碼
import com.cn.xxx.xxx.bean.area.Area;
import com.cn.xxx.xxx.mapper.area.AreaMapper;
import com.cn.xxx.xxx.service.area.AreaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author LiJing
* @ClassName: LCAreaIdToArea
* @date 2019/07/02 11:12
*/
@Component
public class AreaCache extends GuavaAbstractLoadingCache<Long, Area> implements ILocalCache<Long, Area> {
@Autowired
private AreaService areaService;
//由Spring來維持單例模式
private AreaCache() {
setMaximumSize(4000); //最大緩存條數
}
@Override
public Area get(Long key) {
try {
Area value = getValue(key);
return value;
} catch (Exception e) {
logger.error("沒法根據baseDataKey={}獲取Area,多是數據庫中無該記錄。", key, e);
return null;
}
}
/**
* 從數據庫中獲取數據
*/
@Override
protected Area fetchData(Long key) {
logger.debug("測試:正在從數據庫中獲取area,area id={}", key);
return areaService.getAreaInfo(key);
}
}
複製代碼
至此,以上就完成了,簡單緩存搭建,就可使用了. 其原理就是就是先從緩存中查詢,沒有就去數據庫中查詢放入緩存,再去維護緩存,基於你設置的屬性,只需集成緩存實現接口就能夠擴展緩存了............上面就是舉個栗子spring
import com.cn.xxx.common.core.page.PageParams;
import com.cn.xxx.common.core.page.PageResult;
import com.cn.xxx.common.core.util.SpringContextUtil;
import com.google.common.cache.CacheStats;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
/**
* @ClassName: GuavaCacheManager
* @author LiJing
* @date 2019/07/02 11:17
*
*/
public class GuavaCacheManager {
//保存一個Map: cacheName -> cache Object,以便根據cacheName獲取Guava cache對象
private static Map<String, ? extends GuavaAbstractLoadingCache<Object, Object>> cacheNameToObjectMap = null;
/**
* 獲取全部GuavaAbstractLoadingCache子類的實例,即全部的Guava Cache對象
* @return
*/
@SuppressWarnings("unchecked")
private static Map<String, ? extends GuavaAbstractLoadingCache<Object, Object>> getCacheMap(){
if(cacheNameToObjectMap==null){
cacheNameToObjectMap = (Map<String, ? extends GuavaAbstractLoadingCache<Object, Object>>) SpringContextUtil.getBeanOfType(GuavaAbstractLoadingCache.class);
}
return cacheNameToObjectMap;
}
/**
* 根據cacheName獲取cache對象
* @param cacheName
* @return
*/
private static GuavaAbstractLoadingCache<Object, Object> getCacheByName(String cacheName){
return (GuavaAbstractLoadingCache<Object, Object>) getCacheMap().get(cacheName);
}
/**
* 獲取全部緩存的名字(即緩存實現類的名稱)
* @return
*/
public static Set<String> getCacheNames() {
return getCacheMap().keySet();
}
/**
* 返回全部緩存的統計數據
* @return List<Map<統計指標,統計數據>>
*/
public static ArrayList<Map<String, Object>> getAllCacheStats() {
Map<String, ? extends Object> cacheMap = getCacheMap();
List<String> cacheNameList = new ArrayList<>(cacheMap.keySet());
Collections.sort(cacheNameList);//按照字母排序
//遍歷全部緩存,獲取統計數據
ArrayList<Map<String, Object>> list = new ArrayList<>();
for(String cacheName : cacheNameList){
list.add(getCacheStatsToMap(cacheName));
}
return list;
}
/**
* 返回一個緩存的統計數據
* @param cacheName
* @return Map<統計指標,統計數據>
*/
private static Map<String, Object> getCacheStatsToMap(String cacheName) {
Map<String, Object> map = new LinkedHashMap<>();
GuavaAbstractLoadingCache<Object, Object> cache = getCacheByName(cacheName);
CacheStats cs = cache.getCache().stats();
NumberFormat percent = NumberFormat.getPercentInstance(); // 創建百分比格式化用
percent.setMaximumFractionDigits(1); // 百分比小數點後的位數
map.put("cacheName", cacheName);//Cache名稱
map.put("size", cache.getCache().size());//當前數據量
map.put("maximumSize", cache.getMaximumSize());//最大緩存條數
map.put("survivalDuration", cache.getExpireAfterWriteDuration());//過時時間
map.put("hitCount", cs.hitCount());//命中次數
map.put("hitRate", percent.format(cs.hitRate()));//命中比例
map.put("missRate", percent.format(cs.missRate()));//讀庫比例
map.put("loadSuccessCount", cs.loadSuccessCount());//成功加載數
map.put("loadExceptionCount", cs.loadExceptionCount());//成功加載數
map.put("totalLoadTime", cs.totalLoadTime()/1000000); //總加載毫秒ms
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if(cache.getResetTime()!=null){
map.put("resetTime", df.format(cache.getResetTime()));//重置時間
LocalDateTime localDateTime = LocalDateTime.ofInstant(cache.getResetTime().toInstant(), ZoneId.systemDefault()).plusMinutes(cache.getTimeUnit().toMinutes(cache.getExpireAfterWriteDuration()));
map.put("survivalTime", df.format(Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant())));//失效時間
}
map.put("highestSize", cache.getHighestSize());//歷史最高數據量
if(cache.getHighestTime()!=null){
map.put("highestTime", df.format(cache.getHighestTime()));//最高數據量時間
}
return map;
}
/**
* 根據cacheName清空緩存數據
* @param cacheName
*/
public static void resetCache(String cacheName){
GuavaAbstractLoadingCache<Object, Object> cache = getCacheByName(cacheName);
cache.getCache().invalidateAll();
cache.setResetTime(new Date());
}
/**
* 分頁得到緩存中的數據
* @param pageParams
* @return
*/
public static PageResult<Object> queryDataByPage(PageParams<Object> pageParams) {
PageResult<Object> data = new PageResult<>(pageParams);
GuavaAbstractLoadingCache<Object, Object> cache = getCacheByName((String) pageParams.getParams().get("cacheName"));
ConcurrentMap<Object, Object> cacheMap = cache.getCache().asMap();
data.setTotalRecord(cacheMap.size());
data.setTotalPage((cacheMap.size()-1)/pageParams.getPageSize()+1);
//遍歷
Iterator<Map.Entry<Object, Object>> entries = cacheMap.entrySet().iterator();
int startPos = pageParams.getStartPos()-1;
int endPos = pageParams.getEndPos()-1;
int i=0;
Map<Object, Object> resultMap = new LinkedHashMap<>();
while (entries.hasNext()) {
Map.Entry<Object, Object> entry = entries.next();
if(i>endPos){
break;
}
if(i>=startPos){
resultMap.put(entry.getKey(), entry.getValue());
}
i++;
}
List<Object> resultList = new ArrayList<>();
resultList.add(resultMap);
data.setResults(resultList);
return data;
}
}
複製代碼
import com.alibaba.dubbo.config.annotation.Service;
import com.cn.xxx.xxx.cache.GuavaCacheManager;
import com.cn.xxx.xxx.service.cache.CacheService;
import java.util.ArrayList;
import java.util.Map;
/**
* @ClassName: CacheServiceImpl
* @author lijing
* @date 2019.07.06 下午 5:29
*
*/
@Service(version = "1.0.0")
public class CacheServiceImpl implements CacheService {
@Override
public ArrayList<Map<String, Object>> getAllCacheStats() {
return GuavaCacheManager.getAllCacheStats();
}
@Override
public void resetCache(String cacheName) {
GuavaCacheManager.resetCache(cacheName);
}
}
複製代碼
import com.alibaba.dubbo.config.annotation.Reference;
import com.cn.xxx.common.core.page.JsonResult;
import com.cn.xxx.xxx.service.cache.CacheService;
import com.github.pagehelper.PageInfo;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @ClassName: CacheAdminController
* @author LiJing
* @date 2018/07/06 10:10
*
*/
@Controller
@RequestMapping("/cache")
public class CacheAdminController {
@Reference(version = "1.0.0")
private CacheService cacheService;
@GetMapping("")
@RequiresPermissions("cache:view")
public String index() {
return "admin/system/cache/cacheList";
}
@PostMapping("/findPage")
@ResponseBody
@RequiresPermissions("cache:view")
public PageInfo findPage() {
return new PageInfo<>(cacheService.getAllCacheStats());
}
/**
* 清空緩存數據、並返回清空後的統計信息
* @param cacheName
* @return
*/
@RequestMapping(value = "/reset", method = RequestMethod.POST)
@ResponseBody
@RequiresPermissions("cache:reset")
public JsonResult cacheReset(String cacheName) {
JsonResult jsonResult = new JsonResult();
cacheService.resetCache(cacheName);
jsonResult.setMessage("已經成功重置了" + cacheName + "!");
return jsonResult;
}
/**
* 查詢cache統計信息
* @param cacheName
* @return cache統計信息
*/
/*@RequestMapping(value = "/stats", method = RequestMethod.POST)
@ResponseBody
public JsonResult cacheStats(String cacheName) {
JsonResult jsonResult = new JsonResult();
//暫時只支持獲取所有
switch (cacheName) {
case "*":
jsonResult.setData(GuavaCacheManager.getAllCacheStats());
jsonResult.setMessage("成功獲取了全部的cache!");
break;
default:
break;
}
return jsonResult;
}*/
/**
* 返回全部的本地緩存統計信息
* @return
*/
/*@RequestMapping(value = "/stats/all", method = RequestMethod.POST)
@ResponseBody
public JsonResult cacheStatsAll() {
return cacheStats("*");
}*/
/**
* 分頁查詢數據詳情
* @param params
* @return
*/
/*@RequestMapping(value = "/queryDataByPage", method = RequestMethod.POST)
@ResponseBody
public PageResult<Object> queryDataByPage(@RequestParam Map<String, String> params){
int pageSize = Integer.valueOf(params.get("pageSize"));
int pageNo = Integer.valueOf(params.get("pageNo"));
String cacheName = params.get("cacheName");
PageParams<Object> page = new PageParams<>();
page.setPageSize(pageSize);
page.setPageNo(pageNo);
Map<String, Object> param = new HashMap<>();
param.put("cacheName", cacheName);
page.setParams(param);
return GuavaCacheManager.queryDataByPage(page);
}*/
}
複製代碼
以上就是gauva緩存,在後臺中咱們能夠重啓和清除緩存,管理每個緩存和查看緩存的統計信息,常常用於緩存一些不常常改變的數據 寫的簡陋,歡迎你們抨擊~ 下面是一個後臺頁面展現:數據庫