缓存的使用是解决高并发问题的一个重要途径,所以缓存很重要。一般情况下使用本地缓存,如ehcache,guava等就可以了,但是针对分布式、集群架构,本地缓存无法做到相互之间数据保持一致,如果使用redis缓存,则需要不断的去连接redis,这个中间也是有一定的资源消耗,在并发较小的时候这些消耗不影响系统,但是并发较大时就可能导致无法获取到redis连接,从而导致系统奔溃等等问题。
要想搭建自己的二级缓存平台就需要了解spring boot的cache实现机制,cache的实现核心就是org.springframework.cache.Cache接口和org.springframework.cache.CacheManager接口,前者是缓存类,后者是管理缓存的,所以只要我们重写了这两个类,那么就可以实现缓存的重写。
cache提供了一下方法
String getName();
Object getNativeCache();
Cache.ValueWrapper get(Object var1);
T get(Object var1, Class var2);
T get(Object var1, Callable var2);
void put(Object var1, Object var2);
Cache.ValueWrapper putIfAbsent(Object var1, Object var2);
void evict(Object var1);
void clear();
这里我们只需要重写这些方法就行了,由于我们要实现二级缓存,所以要引入本地缓存和redis 缓存,也就是我们只需要在自定义的cache中实现本地缓存和redis 缓存同时使用就可以了。具体实现如下:
package com.wangcongming.cache.core.layering;
import com.alibaba.fastjson.JSON;
import com.wangcongming.cache.core.redis.cache.CustomizedRedisCache;
import com.wangcongming.cache.core.redis.listener.ChannelTopicEnum;
import com.wangcongming.cache.core.redis.listener.RedisPublisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.AbstractValueAdaptingCache;
import org.springframework.cache.support.NullValue;
import org.springframework.data.redis.core.RedisOperations;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* @author wangcongming
* @Package com.wangcongming.cache.core.layering
* @Description: 双缓存类 代码来源于网络,并在此基础做了修改
* @date 2018/5/18 15:16
*/
public class LayeringCache extends AbstractValueAdaptingCache {
Logger logger = LoggerFactory.getLogger(LayeringCache.class);
/**
* 缓存的名称
*/
private final String name;
/**
* 是否使用一级缓存
*/
private boolean usedFirstCache = true;
/**
* redi缓存
*/
private final CustomizedRedisCache redisCache;
/**
* Caffeine缓存
*/
private final CaffeineCache caffeineCache;
RedisOperations extends Object, ? extends Object> redisOperations;
/**
* @param name 缓存名称
* @param prefix 缓存前缀
* @param redisOperations 操作Redis的RedisTemplate
* @param expiration redis缓存过期时间
* @param allowNullValues 是否允许存NULL,默认是false
* @param usedFirstCache 是否使用一级缓存,默认是true
* @param caffeineCache Caffeine缓存
*/
public LayeringCache(String name, byte[] prefix, RedisOperations extends Object, ? extends Object> redisOperations,
long expiration, boolean allowNullValues, boolean usedFirstCache,
com.github.benmanes.caffeine.cache.Cache
从上述代码我们可以看到,在LayeringCache的构造方法中我们直接创建了CaffeineCache和redisCache(这里的rediscache进行了重写),然后在get方法中,先去取本地缓存,没有才会去查询redisCache
现在缓存已经有了,那么怎么去实现缓存的创建呢,这里就要使用cachemanager来实现缓存的管理。
2.重写CacheManager
cachemanager是来管理缓存的,他负责cache的创建
由于我们重写了cache,那么原有的cachemanager就无法再创建此cache,所以需要重写。
首先我们来看cachemanager的具体方法有哪些
package org.springframework.cache;
import java.util.Collection;
public interface CacheManager {
Cache getCache(String var1);
Collection getCacheNames();
}
我们可以看到cachemanager接口只提供了两个方法,一个是获取cache,一个是获取所有cache name。
也就是我们只需要重写这两个方法就可以了
具体实现如下:
private final ConcurrentMap cacheMap = new ConcurrentHashMap(16);
private Caffeine
public LayeringCacheManager(RedisOperations redisOperations) {
this(redisOperations, Collections.emptyList());
}
public LayeringCacheManager(RedisOperations redisOperations, Collection cacheNames) {
this(redisOperations, cacheNames, false);
}
public LayeringCacheManager(RedisOperations redisOperations, Collection cacheNames, boolean allowNullValues) {
this.allowNullValues = allowNullValues;
this.redisOperations = redisOperations;
setCacheNames(cacheNames);
}
@Override
public Cache getCache(String name) {
Cache cache = this.cacheMap.get(name);
if (cache == null && this.dynamic) {
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
cache = createCache(name);
this.cacheMap.put(name, cache);
}
}
}
return cache;
}
protected Cache createCache(String name) {
return new LayeringCache(name, (usePrefix ? cachePrefix.prefix(name) : null), redisOperations,
getSecondaryCacheExpirationSecondTime(name), isAllowNullValues(), getUsedFirstCache(name),
createNativeCaffeineCache(name));
}
@Override
public Collection getCacheNames() {
return Collections.unmodifiableSet(this.cacheMap.keySet());
}
如此我们就实现了二级缓存
注:部分代码来源于网络