项目中如果有使用大量的本地缓存场景,可以使用redis+ehcache组合缓存,优先使用ehcache本地缓存,本地缓存没有查询到再使用redis缓存
可看前文中如何集成
本地缓存使用存在的问题
1、本地缓存如何保证缓存的是最新值
可定义版本号、自增id或者时间戳,进行判断比对是否是最新值
2、各个节点保证本地缓存一致性
保证各个节点的一致性,且不影响性能,常使用消息进行发布订阅或者是广播模式进行同步
c
public class CustomerCache implements org.springframework.cache.Cache {
void evict(Object key);
void put(Object key, @Nullable Object value);
<T> T get(Object key, Callable<T> valueLoader);
}
针对以上3个主要方法,
PUT
c
void put(Object key, @Nullable Object value){
// 数据都得保存一份到redis
boolean success = redis.put(key, expire, value);
// 存入版本号
redis.put(newKey, expire, remoteVersion);
// 以上2步骤应开启redis事务,或可存入hset格式
Long remoteVersion = getRemoteVersion(key);
if (success) {
// 存入本地缓存
ehCacheClient.put(cacheName, prefix + key, remoteVersion);
ehCacheClient.put(cacheName, key, value);
// 发出消息,message需包含key remoteVersion,操作类型,put或delete
messageService.send(topic, message);
// 注册消息监听
messageService.registerMessageListener(message -> {
//删除缓存
if (operate == delete) {
ehCacheClient.remove(cacheName, key);
ehCacheClient.remove(cacheName, prefix + key);
return;
}
// 更新缓存
Long localVersion = ehCacheClient.get(cacheName, prefix + key);
if (remoteVersion > localVersion) {
ehCacheClient.put(cacheName, key, remoteValue);
ehCacheClient.put(cacheName, prefix + key, remoteVersion);
}
});
}
}
GET
c
<T> T get(Object key, Callable<T> valueLoader){
value = (T) ehCacheClient.get(cacheName, key)
if (value == null) {
value = redis.get(key);
// 重新增加本地缓存
ehCacheClient.put(cacheName, key, value);
ehCacheClient.put(cacheName, prefix + key, value);
}
}
EVICT
c
void evict(Object key){
ehCacheClient.remove(cacheName, key);
ehCacheClient.remove(cacheName, prefix + key);
redis.remote(key);
// 同步到其他节点
messageService.send(topic, message);
}