1.引入Maven - 版本自行选择最新
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
2.案例 - [ 部分核心代码 ]
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.apache.log4j.Logger;
import java.util.concurrent.TimeUnit;
public class MemoryCacheUtils {
private final static Logger logger = Logger.getLogger(MemoryCacheUtils.class);
private static LoadingCache<Long, MyResult> myMemoryCache = null;
static{
myMemoryCache = CacheBuilder.newBuilder() //CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例
.concurrencyLevel(500) //设置并发级别为8,并发级别是指可以同时写缓存的线程数
.expireAfterWrite(10, TimeUnit.MINUTES) //设置写缓存后10分钟过期
.refreshAfterWrite(1, TimeUnit.SECONDS) //设置写缓存后1秒钟刷新
.initialCapacity(500) //设置缓存容器的初始容量为5
.maximumSize(10000) //设置缓存最大容量为10000,超过10000之后就会按照LRU最近虽少使用算法来移除缓存项
.recordStats() //设置要统计缓存的命中率
// .removalListener(new RemovalListener<Object, Object>() { //设置缓存的移除通知
// @Override
// public void onRemoval(RemovalNotification<Object, Object> notification) {
// System.out.println("内存缓存:"+notification.getKey() + " 被移除了,原因: " + notification.getCause());
// }
// })
.build(//build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
new CacheLoader<Long, MyResult>() {
@Override
public MyResult load(Long key) throws Exception {
// System.out.println("缓存没有时,从数据库加载" + key);
// return new MyResult();
return null;
}
}
);
}
public static void putCache(Long skuId,MyResult video){
myMemoryCache.put(skuId,video);
}
public static MyResult getCache(Long skuId){
try{
return myMemoryCache.get(skuId);
}catch(Exception e){
if(e instanceof CacheLoader.InvalidCacheLoadException){
logger.error(String.format("缓存中没有该值:%s [%s]",e.getMessage(),skuId));
}else{
logger.error(String.format("通过内存缓存获取失败:%s [%s]",e.getMessage(),skuId),e);
}
return null;
}
}
public static void main(String[] args) throws Exception {
MyResult result = MemoryCacheUtils.getCache(123L);
System.out.println(JSONObject.toJSONString(result));
MyResult cache = new MyResult();
TraceVideoData data = new TraceVideoData();
data.setSkuId(123L);
cache.setData(data);
cache.setResult(esult_Status.SUCCESS);
MemoryCacheUtils.putCache(123L,cache);
result = MemoryCacheUtils.getCache(123L);
System.out.println(JSONObject.toJSONString(result));
System.out.println("size:"+MemoryCacheUtils.myMemoryCache.size());
}
}
LoadingCache是不支持缓存null值的;如果load回调方法返回null,则在get的时候会抛出异常 - 需要程序捕获处理;
3.缓存移除机制
guava做cache时候数据的移除分为被动移除和主动移除两种。
被动移除分为三种:
-
基于大小的移除:数量达到指定大小,会把不常用的键值移除
-
基于时间的移除:expireAfterAccess(long, TimeUnit) 根据某个键值对最后一次访问之后多少时间后移除
expireAfterWrite(long, TimeUnit) 根据某个键值对被创建或值被替换后多少时间移除 -
基于引用的移除:主要是基于java的垃圾回收机制,根据键或者值的引用关系决定移除
主动移除分为三种:1).单独移除:Cache.invalidate(key)
2).批量移除:Cache.invalidateAll(keys)
3).移除所有:Cache.invalidateAll()
如果配置了移除监听器RemovalListener,则在所有移除的动作时会同步执行该listener下的逻辑。
如需改成异步,使用:RemovalListeners.asynchronous(RemovalListener, Executor)
4.说明
在项目中大家经常使用redis作为缓存,但是在某些高并发的场景下redis也会出现瓶颈 造成热key等因素致使redis链接超时导致整个业务线超时,所有必填的情况下添加内存缓存可以大大缓解压力使业务更稳定;