使用Guava作爲本地緩存讓系統飛起來

簡介

guava是google的一個開源java框架,其github地址是 https://github.com/google/guava。guava工程包含了若干被Google的Java項目廣泛依賴的核心庫,例如:

  • 集合 [collections]
  • 緩存 [caching]
  • 原生類型支持 [primitives support]
  • 併發庫 [concurrency libraries]
  • 通用註解 [common annotations]
  • 字符串處理 [string processing]
  • I/O

所有這些工具每天都在被Google的工程師應用在產品服務中。 其中caching這一塊是常用的模塊的之一

引入依賴

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>27.1-jre</version>
</dependency>

使用示例

基本用法

@Component
public final class WsPlasRoleCache {
	private static Logger logger = LoggerFactory.getLogger(WsPlasRoleCache.class);
	private static AtomicBoolean isInited = new AtomicBoolean(false);
	private static LoadingCache<String, List<WsPlasRole>> cache;

	private WsPlasRoleCache() {
	    if (!isInited.get()) {
            init();
        }
	}

	private static void init() {
		logger.info("---初始化WsPlasRole");
		cache = CacheBuilder.newBuilder().refreshAfterWrite(10, TimeUnit.MINUTES)
				.build(new CacheLoader<String, List<WsPlasRole>>() {
					@Override
					public List<WsPlasRole> load(String key) throws Exception {
					    // 如果是第一次,調用load方法;如果是兩次get時間在10分鐘之間,就不會調用load
						PlasRoleManager service = SpringContextHolder.getBean("plasRoleManager");
						logger.info("查詢WsPlasRole");
						List<WsPlasRole> list = service.getWsAll(key);
						return list;
					}
				});
		isInited.set(true);
	}


	public static List<WsPlasRole> getWsAll(String key) {
		try {
			return cache.get(key);
		} catch (Exception e) {
		}
		return null;
	}
}

利用@Component讓工程啓動的時候初始化cache對象,重寫構造函數並且設置爲private是爲了保證是單例的

核心方法

  • refreshAfterWrite : 寫入數據後多久過期,只阻塞當前數據加載線程,其他線程返回舊值
  • expireAfterWrite : 寫緩存後多久過期
  • expireAfterAccess : 讀寫緩存後多久過期
  • maximumSize : 設置緩存最大容量,超過之後就會按照LRU最近雖少使用算法來移除緩存項

異步刷新

抽象類CacheLoader是同步刷新的。

如果緩存到期了,有多個線程調用緩存,那麼第一個調用的會阻塞住,其他的線程會直接返回已過期的緩存

如果想要讓所有的線程都不阻塞,就要創建異步加載類

public abstract class RefreshAsyncCacheLoader<K, V> extends CacheLoader<K, V> {
    private ExecutorService threadPool = Executors.newSingleThreadExecutor();
    @Override
    public ListenableFuture<V> reload(final K key, final V oldValue) throws Exception {
        checkNotNull(key);
        checkNotNull(oldValue);
        //增加配置參數,控制是否異步加載;1-異步加載,0-同步加載
        String async = PropertyConfigurer.getProperty("cache.refres.async");
        if (StringUtils.equals(async,"1")){
            ListenableFutureTask<V> task = ListenableFutureTask.create(new Callable<V>() {
                public V call() {
                    try {
                        return load((K) key);
                    } catch (Exception e) {
                    }
                    return oldValue;
                }
            });
            threadPool.execute(task);
            return task;
        }else{
            return Futures.immediateFuture(load(key));
        }

    }

}

將CacheLoader替換成異步實現類RefreshAsyncCacheLoader

public static void init() {
	logger.info("---初始化WsPlasUserCache");
	cache = CacheBuilder.newBuilder().refreshAfterWrite(10, TimeUnit.MINUTES)
			.build(new RefreshAsyncCacheLoader<String, List<WsPlasUser>>() {
				@Override
				public List<WsPlasUser> load(String key) throws Exception {
					PlasUserManager service = SpringContextHolder.getBean("plasUserManager");
					logger.info("查詢WsPlasUser");
					List<WsPlasUser> list = service.getWsAll();
					return list;
				}
			});
}

顯示清除

緩存會自動過期,也可以手動讓其過期,主要有以下方法:

  • 個別清除:Cache.invalidate(key)
  • 批量清除:Cache.invalidateAll(keys)
  • 清除所有緩存項:Cache.invalidateAll()

總結

  • guava cache是本地緩存,和redis、memcached等集中緩存不同,如果是集羣環境,本地緩存有不同步的問題,如果對緩存同步時間要求不是太高,這完全不是問題
  • 相比於redis等集中緩存,本地緩存性能更高;因爲沒有網絡交互,直接內存讀取
  • guava cache使用簡單、控制靈活
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章