guava缓存
google guava-cache
redis做缓存很快,但是还有网络IO,在并发量大要求耗时低的情况下还是不满足需求,所以想试试本地缓存。
发现guava缓存不错,想试试
在本地demo测试的时候发现更新缓存时有毛刺出现
package cn.wkq.cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.junit.Test;
import java.util.concurrent.*;
/**
* LocalCache
*
* @author: weikeqin.cn@gmail.com
* @date: 2020-05-10 17:08
**/
public class LocalCache {
/**
*
*/
private transient static final Logger log = LoggerFactory.getLogger(LocalCache.class);
/**
*
*/
private static LoadingCache<String, Object> cache = null;
static {
StringBuilder sb = new StringBuilder(1 << 20);
for (int i = 0; i < 102400; i++) {
sb.append(i);
}
String testString = sb.toString();
log.info("{}", testString.length());
cache = CacheBuilder.newBuilder()
//设置并发级别为8,并发级别是指可以同时写缓存的线程数
.concurrencyLevel(8)
//设置缓存容器的初始容量为10
.initialCapacity(10)
//设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
.maximumSize(100)
//是否需要统计缓存情况,该操作消耗一定的性能,生产环境应该去除
.recordStats()
//设置写缓存后n秒钟过期
.expireAfterWrite(1, TimeUnit.SECONDS)
//设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
//.expireAfterAccess(17, TimeUnit.SECONDS)
//只阻塞当前数据加载线程,其他线程返回旧值
.refreshAfterWrite(1, TimeUnit.SECONDS)
//设置缓存的移除通知
.removalListener(notification -> {
log.info(" 本地缓存key:{} 被移除,原因:{} ", notification.getKey(), notification.getCause());
//log.info(" 本地缓存key:{} 被移除,原因:{} 对应值:{}", notification.getKey(), notification.getCause(), notification.getValue());
})
//build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) throws Exception {
log.info("重新查库");
return "获取的缓存的值" + testString;
}
});
}
/**
*
*/
@Test
public void cacheTest() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 8, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
String key = "1";
int count = 100000;
for (int i = 0; i < count; i++) {
int finalI = i;
executor.execute(() -> {
long t1 = System.currentTimeMillis();
Object value = null;
try {
value = cache.get(key);
} catch (ExecutionException e) {
log.error("", e);
}
long t2 = System.currentTimeMillis();
log.info(" {} cost {} ms ", finalI, t2 - t1);
//log.info("{} 根据 {} 获取到的值 {} ", Thread.currentThread().getName(), key, ((String) value).length());
});
// try {
// Thread.sleep(1);
// } catch (InterruptedException e) {
// log.error("", e);
// }
}
executor.shutdown();
log.info("线程池关闭。");
while (!executor.isTerminated()) {
try {
TimeUnit.SECONDS.sleep(1);
log.info("线程池中线程数目:{},队列中等待执行的任务数目:{},已执行玩别的任务数目:{}", executor.getPoolSize(), executor.getQueue().size(), executor.getCompletedTaskCount());
} catch (InterruptedException e) {
log.error("", e);
}
log.info("还有线程未执行完成");
}
//缓存状态查看
log.info(cache.stats().toString());
}
}
少了网络IO,整体 TP99 比redis好了不少
但是在压测时发现毛刺更严重
References
[1] google/guava/wiki/CachesExplained
[2] [Google Guava] 3-缓存 并发编程网 - ifeve.com
[3] 缓存篇 : Guava cache 之全面剖析