基于Guava实现的本地缓存

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);
        }
    }

}
posted @ 2022-03-15 17:39  叮叮叮叮叮叮当  阅读(74)  评论(0编辑  收藏  举报