一、本地缓存设计
1、Nacos的缓存结构设计为例,通用缓存为例,支持不同模式的缓存,
支持更复杂功能,可以直接使用Guava和Caffine直接使用.
①、Cache缓存接口.
package com.alibaba.nacos.common.cache;
import java.util.concurrent.Callable;
/**
* Cache method collection definition.
* @author zzq
* @date 2021/7/30
*/
public interface Cache<K, V> {
/**
* Cache a pair of key value. If the key value already exists, the value will be overwritten.
* @param key cache key
* @param val cache value
*/
void put(K key, V val);
/**
* Take the corresponding value from the cache according to the cache key.
* @param key cache key
* @return cache value
*/
V get(K key);
/**
* Get the value in the cache according to the primary key, and put it into the cache after processing by the function.
* @param key cache key
* @param call a function, the return value of the function will be updated to the cache
* @return cache value
* @throws Exception callable function interface throw exception
*/
V get(K key, Callable<? extends V> call) throws Exception;
/**
* Take the corresponding value from the cache according to the cache key, and remove this record from the cache.
* @param key cache key
* @return cache value
*/
V remove(K key);
/**
* Clear the entire cache.
*/
void clear();
/**
* Returns the number of key-value pairs in the cache.
* @return number of key-value pairs
*/
int getSize();
}
②、CacheItemProperties【通用缓存项配置】
package com.alibaba.nacos.common.cache.builder;
/**
* Cache item's own attributes.
* @author zzq
* @date 2021/7/30
*/
public class CacheItemProperties {
private long expireNanos;
public long getExpireNanos() {
return expireNanos;
}
public void setExpireNanos(long expireNanos) {
this.expireNanos = expireNanos;
}
}
2、缓存实现
①、SimpleCache,不带任何功能.
package com.alibaba.nacos.common.cache.impl;
import com.alibaba.nacos.common.cache.Cache;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* Simple implementation of {@code Cache}.
* @author zzq
* @date 2021/7/30
*/
public class SimpleCache<K, V> implements Cache<K, V> {
private Map<K, V> cache;
public SimpleCache(int size) {
cache = new HashMap(size);
}
@Override
public void put(K key, V val) {
cache.put(key, val);
}
@Override
public V get(K key) {
return cache.get(key);
}
@Override
public V get(K key, Callable<? extends V> call) throws Exception {
if (cache.containsKey(key)) {
return cache.get(key);
} else {
V v2 = call.call();
cache.put(key, v2);
return v2;
}
}
@Override
public V remove(K key) {
return cache.remove(key);
}
@Override
public void clear() {
cache.clear();
}
@Override
public int getSize() {
return cache.size();
}
}
②、AutoExpireCache,实现过期清理.
package com.alibaba.nacos.common.cache.decorators;
import com.alibaba.nacos.common.cache.Cache;
import com.alibaba.nacos.common.cache.builder.CacheItemProperties;
import java.util.HashMap;
import java.util.concurrent.Callable;
/**
* A wrapper that automatically expires the cache.
* @author zzq
* @date 2021/7/30
*/
public class AutoExpireCache<K, V> implements Cache<K, V> {
private long expireNanos;
private Cache<K, V> delegate;
private HashMap<K, CacheItemProperties> keyProp = new HashMap<>();
public AutoExpireCache(Cache<K, V> delegate, long expireNanos) {
this.expireNanos = expireNanos;
this.delegate = delegate;
}
@Override
public void put(K key, V val) {
keyProp.put(key, cacheItemProperties());
this.delegate.put(key, val);
}
@Override
public V get(K key) {
if (keyProp.get(key) != null && isExpire(keyProp.get(key))) {
this.keyProp.remove(key);
this.delegate.remove(key);
return null;
}
return this.delegate.get(key);
}
@Override
public V get(K key, Callable<? extends V> call) throws Exception {
V cachedValue = this.get(key);
if (null == cachedValue) {
V v2 = call.call();
this.put(key, v2);
return v2;
}
return cachedValue;
}
@Override
public V remove(K key) {
keyProp.remove(key);
return this.delegate.remove(key);
}
@Override
public void clear() {
keyProp.clear();
this.delegate.clear();
}
@Override
public int getSize() {
return this.delegate.getSize();
}
private boolean isExpire(CacheItemProperties itemProperties) {
return expireNanos != -1 && (System.nanoTime() - itemProperties.getExpireNanos() > expireNanos);
}
private CacheItemProperties cacheItemProperties() {
CacheItemProperties cacheItemProperties = new CacheItemProperties();
cacheItemProperties.setExpireNanos(System.nanoTime());
return cacheItemProperties;
}
}
③、SynchronizedCache,【线程安全的缓存】.
package com.alibaba.nacos.common.cache.decorators;
import com.alibaba.nacos.common.cache.Cache;
import java.util.concurrent.Callable;
/**
* A wrapper that thread-safe cache.
* @author zzq
* @date 2021/7/30
*/
public class SynchronizedCache<K, V> implements Cache<K, V> {
private final Cache<K, V> delegate;
public SynchronizedCache(Cache<K, V> delegate) {
this.delegate = delegate;
}
@Override
public synchronized void put(K key, V val) {
this.delegate.put(key, val);
}
@Override
public synchronized V get(K key) {
return this.delegate.get(key);
}
@Override
public V get(K key, Callable<? extends V> call) throws Exception {
return this.delegate.get(key, call);
}
@Override
public synchronized V remove(K key) {
return this.delegate.remove(key);
}
@Override
public synchronized void clear() {
this.delegate.clear();
}
@Override
public synchronized int getSize() {
return this.delegate.getSize();
}
}
④、LruCache【LRU缓存】
package com.alibaba.nacos.common.cache.decorators;
import com.alibaba.nacos.common.cache.Cache;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* A wrapper that lru cache.
* @author zzq
* @date 2021/7/30
*/
public class LruCache<K, V> implements Cache<K, V> {
private final Cache<K, V> delegate;
private Map<K, V> keyMap;
private K eldestKey;
public LruCache(Cache<K, V> delegate, int size) {
this.delegate = delegate;
setSize(size);
}
@Override
public int getSize() {
return delegate.getSize();
}
public void setSize(final int size) {
keyMap = new LinkedHashMap<K, V>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
boolean tooBig = size() > size;
if (tooBig) {
eldestKey = eldest.getKey();
}
return tooBig;
}
};
}
@Override
public void put(K key, V val) {
delegate.put(key, val);
cycleKeyList(key);
}
@Override
public V get(K key) {
keyMap.get(key);
return delegate.get(key);
}
@Override
public V get(K key, Callable<? extends V> call) throws Exception {
return this.delegate.get(key, call);
}
@Override
public V remove(K key) {
return delegate.remove(key);
}
@Override
public void clear() {
delegate.clear();
keyMap.clear();
}
private void cycleKeyList(K key) {
keyMap.put(key, null);
if (eldestKey != null) {
delegate.remove(eldestKey);
eldestKey = null;
}
}
}
3、缓存建造器,选择不同缓存.
package com.alibaba.nacos.common.cache.builder;
import com.alibaba.nacos.common.cache.Cache;
import com.alibaba.nacos.common.cache.decorators.AutoExpireCache;
import com.alibaba.nacos.common.cache.decorators.LruCache;
import com.alibaba.nacos.common.cache.decorators.SynchronizedCache;
import com.alibaba.nacos.common.cache.impl.SimpleCache;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* Cache builder.
* @author zzq
* @date 2021/7/30
*/
public class CacheBuilder<K, V> {
private static final int DEFAULT_MAXIMUMSIZE = 1024;
private static final int DEFAULT_INITIALIZE_CAPACITY = 1024;
private static final int DEFAULT_EXPIRE_NANOS = -1;
private long expireNanos = DEFAULT_EXPIRE_NANOS;
private int maximumSize = DEFAULT_MAXIMUMSIZE;
private int initializeCapacity = DEFAULT_INITIALIZE_CAPACITY;
private boolean sync = false;
private boolean lru = false;
public static <K, V> CacheBuilder<K, V> builder() {
return new CacheBuilder<>();
}
/**
* Set expiration time.
*/
public CacheBuilder<K, V> expireNanos(long duration, TimeUnit unit) {
checkExpireNanos(duration, unit);
this.expireNanos = unit.toNanos(duration);
return this;
}
private void checkExpireNanos(long duration, TimeUnit unit) {
if (duration < 0) {
throw new IllegalArgumentException("duration cannot be negative");
}
if (Objects.isNull(unit)) {
throw new IllegalArgumentException("unit cannot be null");
}
}
/**
* Set the maximum capacity of the cache pair.
* @param maximumSize maximum capacity
*/
public CacheBuilder<K, V> maximumSize(int maximumSize) {
if (maximumSize < 0) {
throw new IllegalArgumentException("size cannot be negative");
}
this.maximumSize = maximumSize;
return this;
}
/**
* Set whether the cache method is synchronized.
* @param sync if sync value is true, each method of the constructed cache is synchronized.
*/
public CacheBuilder<K, V> sync(boolean sync) {
this.sync = sync;
return this;
}
/**
* Does the constructed cache support lru.
* @param lru If the cache built for true is an lru cache.
*/
public CacheBuilder<K, V> lru(boolean lru) {
this.lru = lru;
return this;
}
/**
* Set the initial capacity of the cache pair.
* @param initializeCapacity initialize capacity
*/
public CacheBuilder<K, V> initializeCapacity(int initializeCapacity) {
if (initializeCapacity < 0) {
throw new IllegalArgumentException("initializeCapacity cannot be negative");
}
this.initializeCapacity = initializeCapacity;
return this;
}
/**
* Build the cache according to the builder attribute.
*/
public Cache<K, V> build() {
Cache<K, V> cache = new SimpleCache<>(initializeCapacity);
if (lru) {
cache = new LruCache<>(cache, maximumSize);
}
if (expireNanos != -1) {
cache = new AutoExpireCache<>(cache, expireNanos);
}
if (sync) {
cache = new SynchronizedCache<>(cache);
}
return cache;
}
}
private static final Cache<String, RateLimiter> CACHE;
static {
CACHE = CacheBuilder.<String, RateLimiter>builder()
.expireNanos(1, TimeUnit.MINUTES)
.initializeCapacity(CAPACITY_SIZE)
.sync(true)
.build();
}
具体使用