package com.liuhuan.cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 缓存
*
* @author LiuHuan
* @since 2022/3/15
*/
@Slf4j
public abstract class Cache<K, V> {
/**
* 线程池维护线程的最少数量
*/
private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors();
/**
* 线程池维护线程的最大数量
*/
private static final int MAX_POOL_SIZE = 10;
/**
* 空闲线程等待工作的超时时间
*/
private static final long KEEP_ALIVE_TIME = 1000L;
/**
* 线程池所使用的缓冲队列大小
*/
private final static int WORK_QUEUE_SIZE = 10;
ListeningExecutorService backgroundRefreshPools =
MoreExecutors.listeningDecorator(new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE,
KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(WORK_QUEUE_SIZE),
new ThreadFactoryBuilder().setNameFormat("cache-load-thread-%d").build(),
new ThreadPoolExecutor.DiscardPolicy()));
LoadingCache<K, V> cache = CacheBuilder.newBuilder()
.refreshAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(300)
.build(new CacheLoader<K, V>() {
@Override
public V load(K key) throws Exception {
return getValueByKey(key);
}
@Override
public ListenableFuture<V> reload(K key, V oldValue) throws Exception {
return backgroundRefreshPools.submit(() -> getValueByKey(key));
}
});
/**
* 查询缓存
* @param key
* @return value
*/
public V get(K key) {
try {
return cache.get(key);
} catch (Exception e) {
log.info("get cache exception, key:{}, e:{}", key, e);
return null;
}
}
/**
* 根据key查询value
*
* @param key
* @return value
*/
protected abstract V getValueByKey(K key);
/**
* 加载热点数据
* @return map
*/
protected Map<K, V> loadHotspotData() {
return Maps.newHashMap();
}
@PostConstruct
public void initCache() {
Map<K, V> data = loadHotspotData();
log.info("cache load hotspot data:{}", data);
cache.putAll(data);
}
@PreDestroy
public void destroy() {
try {
backgroundRefreshPools.shutdown();
} catch (Exception e) {
log.error("cache load thread pool showdown exception", e);
}
}
}