DoubleCache
DoubleCache 指的是本地+redis两份缓存模式
本地缓存过期之后从redis读取新数据
redis缓存过期时,从业务里读取新数据.
设计原理: 利用 loadingCache的过期刷新来实现异步线程自动刷新,而不阻塞当前数据返回
后期优化: 远程刷新时,增加锁机制来避免多次调用业务数据.
import com.google.common.base.Strings;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors; import com.fasterxml.jackson.databind.JavaType;
import com.ppmoney.ppmon.rotom.utils.text.JsonMapper; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.Assert; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Function; import lombok.extern.slf4j.Slf4j; @Slf4j
public class DoubleCache<V> {
private static ExecutorService executorService = Executors.newFixedThreadPool(5);
private static ListeningExecutorService service = MoreExecutors.listeningDecorator(executorService);
private final int remoteExpireSeconds;
private final int localExpireSeconds;
private final LoadingCache<String, V> remoteCache;
private final LoadingCache<String, V> localCache;
private final V defaultValue;
private final Function<String, V> function;
private final StringRedisTemplate redisTemplate;
private final String business;
private final Class<V> clazz;
private final JavaType javaType;
private final CacheLoader<String, V> remoteCacheLoader = new CacheLoader<String, V>() {
@Override
public V load(String key) throws Exception {
V result = function.apply(key);
String redisKey = getRedisKey(key);
redisTemplate.opsForValue().set(redisKey, JsonMapper.INSTANCE.toJson(result), remoteExpireSeconds,
TimeUnit.SECONDS);
// 本地不存数据,减少内存占用
return defaultValue;
} @Override
public ListenableFuture<V> reload(String key, V oldValue) throws Exception {
log.info("redis缓存刷新.key:{}", key);
ListenableFuture<V> result = service.submit(() -> function.apply(key));
String redisKey = getRedisKey(key);
redisTemplate.opsForValue().set(redisKey, JsonMapper.INSTANCE.toJson(result.get()), remoteExpireSeconds,
TimeUnit.SECONDS);
// 本地不存数据,减少内存占用
return service.submit(() -> defaultValue);
}
}; private final CacheLoader<String, V> localCacheLoader = new CacheLoader<String, V>() {
@Override
public V load(String key) throws Exception {
String redisKey = getRedisKey(key);
String val = redisTemplate.opsForValue().get(redisKey);
if (Strings.isNullOrEmpty(val)) {
remoteCache.get(key);
val = redisTemplate.opsForValue().get(redisKey);
}
if (Strings.isNullOrEmpty(val)) {
return defaultValue;
}
return clazz != null
? JsonMapper.INSTANCE.fromJson(val, clazz)
: JsonMapper.INSTANCE.fromJson(val, javaType);
} @Override
public ListenableFuture<V> reload(String key, V oldValue) throws Exception {
log.info("本地缓存刷新.key:{}", key);
String redisKey = getRedisKey(key);
String val = redisTemplate.opsForValue().get(redisKey);
if (Strings.isNullOrEmpty(val)) {
remoteCache.get(key);
val = redisTemplate.opsForValue().get(redisKey);
}
if (Strings.isNullOrEmpty(val)) {
return service.submit(() -> defaultValue);
}
final V result = clazz != null
? JsonMapper.INSTANCE.fromJson(val, clazz)
: JsonMapper.INSTANCE.fromJson(val, javaType);
return service.submit(() -> result);
}
}; private String getRedisKey(String key) {
return "g2:doubleCache:" + business + ":" + key;
} public DoubleCache(String business,
int localExpireSeconds,
int remoteExpireSeconds,
Function<String, V> function,
StringRedisTemplate redisTemplate,
V defaultV,
Class<V> clazz,
JavaType javaType) {
Assert.isTrue(1 < remoteExpireSeconds, "远程缓存过期时间必须大于1");
Assert.isTrue(0 < localExpireSeconds, "本地缓存过期时间必须大于0");
Assert.isTrue(localExpireSeconds < remoteExpireSeconds, "远程缓存过期时间必须大于本地缓存过期时间");
Assert.isTrue(javaType != null || clazz != null, "clazz与javaType不能同时为空");
Assert.isTrue(defaultV != null, "defaulV不能为空");
Assert.isTrue(function != null, "function不能为空");
Assert.isTrue(redisTemplate != null, "redisTemplate不能为空");
Assert.isTrue(!Strings.isNullOrEmpty(business), "business不能为空");
this.clazz = clazz;
this.javaType = javaType;
this.localExpireSeconds = localExpireSeconds;
this.remoteExpireSeconds = remoteExpireSeconds;
this.business = business;
this.function = function;
this.defaultValue = defaultV;
remoteCache = CacheBuilder.newBuilder()
.maximumSize(10000)
.initialCapacity(100)
.refreshAfterWrite(remoteExpireSeconds - 1, TimeUnit.SECONDS)
.softValues()
.build(remoteCacheLoader);
localCache = CacheBuilder.newBuilder()
.maximumSize(10000)
.initialCapacity(100)
.refreshAfterWrite(localExpireSeconds, TimeUnit.SECONDS)
.softValues()
.build(localCacheLoader);
this.redisTemplate = redisTemplate;
} public V get(String key) {
try {
return localCache.get(key);
} catch (Exception ex) {
log.error("获取缓存异常!", ex);
return null;
}
}
}
DoubleCache的更多相关文章
- android 双缓存机制
废话不多说,直接贴代码! 所谓的双缓存,第一就是缓存在内存里面,第二就是缓存在SD卡里面,当你需要加载数据时,先去内存缓存中查找,如果没有再去SD卡中查找,并且用户可以自选使用哪种缓存! 缓存内存和缓 ...
- jetty 最后版本类库树, 基本上大多数应用都够了
d:\jetty-distribution-8.1.17.v20150415\lib\annotations\javax.annotation-1.1.0.v201108011116.jarjavax ...
- 《Android源码设计模式》学习笔记之ImageLoader
微信公众号:CodingAndroid cnblog:http://www.cnblogs.com/angel88/ CSDN:http://blog.csdn.net/xinpengfei521 需 ...
- Asp.Net Core微服务初体验
ASP.Net Core的基本配置 .在VS中调试的时候有很多修改Web应用运行端口的方法.但是在开发.调试微服务应用的时候可能需要同时在不同端口上开启多个服务器的实例,因此下面主要看看如何通过命令行 ...
- Android为TV端助力 双缓存机制
废话不多说,直接贴代码! 所谓的双缓存,第一就是缓存在内存里面,第二就是缓存在SD卡里面,当你需要加载数据时,先去内存缓存中查找,如果没有再去SD卡中查找,并且用户可以自选使用哪种缓存! 缓存内存和缓 ...
- hbase源码系列(十三)缓存机制MemStore与Block Cache
这一章讲hbase的缓存机制,这里面涉及的内容也是比较多,呵呵,我理解中的缓存是保存在内存中的特定的便于检索的数据结构就是缓存. 之前在讲put的时候,put是被添加到Store里面,这个Store是 ...
- Android图片二级缓存
点击下载源代码 想起刚開始写代码的时候,领导叫我写一个头像下载的方法,当时屁颠屁颠就写了一个图片下载的,每次都要去网络上请求,最后直接被pass掉了 当时的思路是这种 后来渐渐地就知道了有二级缓存这东 ...
- 基本的数据类型分析----java.lang.Number类及其子类分析
本文转自http://blog.csdn.net/springcsc1982/article/details/8788345 感谢作者 编写了一个测试程序,如下: int a = 1000, b= 1 ...
- 13 hbase源码系列(十三)缓存机制MemStore与Block Cache
这一章讲hbase的缓存机制,这里面涉及的内容也是比较多,呵呵,我理解中的缓存是保存在内存中的特定的便于检索的数据结构就是缓存. 之前在讲put的时候,put是被添加到Store里面,这个Store是 ...
随机推荐
- SVN 目录 定义
1.项目名称定义: 项目自然序号_项目名称_负责人名称 例如:23_测试项目_An 2.项目目录定义: 01_Source 注:01_Source 中包含 代码 和 DB 设计 02_Document ...
- JQ的live学习
$("#StartTime").live("blur keypress keyup",function(){ if($("#EndTime" ...
- new做了些什么?
new做了些什么? function People(name, age){ this.name = name; this.age = age; }; var xiaoming = new People ...
- Angular的一些常用命令
Angular的一些常用命令 cmd中创建项目: ng new taskmgr -si --style=scss //先不安装依赖,si 为skip install material需要scss样式的 ...
- ftp服务的安装
ftp服务的安装 1.环境准备 2.安装服务 3.配置文件 3.1.匿名访问 把以下三个匿名上传写入开启 启动并查看服务状态: Linux客户端访问: Tips: 220表示服务正常,可以登陆:230 ...
- rabbitmq 发送的用户名是什么
消费的用户名也必须是什么 接收必须单独启动线程——否则mfc会崩溃 Sleep(30000); //30秒 只要你打开消费队列,只消费1个 队列中所有数据都丢失???????? 断线重连: 知道答案 ...
- 使用canvas时, 如何用相对单位(rem, rpx)来适配不同机型
使用canvas的api时, 使用的都是绝对数值, 如: 方法传参是坐标位置,不带单位,如ctx.translate(10,10); 那么此时, 我就需要将rem或rpx 转换成 px; 首先, 获取 ...
- PHP实现笛卡尔积算法
概念 在数学中,两个集合X和Y的笛卡儿积(Cartesian product),又称直积,表示为 X × Y.设A.B是任意两个集合,在集合A中任意取一个元素x,在集合B中任意取一个元素y,组成一个有 ...
- postgres服务安装,启动和配置
安装以及启动 yum install readline-devel tar xf postgresql-11.1.tar.gz cd postgresql-11.1 ./configure --pre ...
- Ubuntu 16.04 install R language
apt-get install r-base r-base-dev