package com.pingan.haofang.agent.saas.util.cache.redis;
import com.pingan.haofang.agent.saas.common.utils.Log;
import com.pingan.haofang.agent.saas.common.utils.RedisUtils;
import org.apache.log4j.Logger;
import org.springframework.data.redis.core.RedisTemplate;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
/**
* redis缓存
*
*/
public class RedisCache<K, V> {
private static final Logger log = Logger.getLogger(RedisCache.class);
private static final ExecutorService executor = Executors.newCachedThreadPool();
/**
* 对Null类型的值,用户希望设定的过期时间 ,时间单位为秒
*/
private long nullValueExpiredTime = 10;
/** 对于正常的缓存值,用户希望设定的过期时间,时间单位为秒 */
private long valueExpiredTime = 24 * 60 * 60;
/**
* 对于通过getDataByKey接口获取数据是报错的情况,用户希望Null在本地缓存的过期时间,时间单位为秒
*/
private long errorValueExpiredTime = 10;
/**
* RedisCacheDataCallBack的实现,在创建一个DaemonCache,强制要求用户实现
*/
private RedisCacheDataCallBack<K, V> callback;
private RedisTemplate<String, V> redisTemplate;
private RedisUtils redisUtils;
//private RedisUtils<K,V> redisUtils;
/**
* 需要传入RedisCacheDataCallBack构造
*/
public RedisCache(RedisCacheDataCallBack<K, V> redisCacheDataCallBack, RedisTemplate<String, V> redisTemplate) {
if (redisCacheDataCallBack == null) {
throw new IllegalArgumentException("when create a new RedisCache, please implmement the RedisCacheDataCallBack first!");
}
this.callback = redisCacheDataCallBack;
this.redisTemplate = redisTemplate;
}
/**
* 需要传入RedisCacheDataCallBack构造
*/
public RedisCache(RedisCacheDataCallBack<K, V> redisCacheDataCallBack, RedisUtils redisUtils) {
if (redisCacheDataCallBack == null) {
throw new IllegalArgumentException("when create a new RedisCache, please implmement the RedisCacheDataCallBack first!");
}
this.callback = redisCacheDataCallBack;
this.redisUtils = redisUtils;
}
/**
* 创建RedisCache实例 ,默认本地缓存大小为10万
*
* @param nullValueExpiredTime 空数据过期时间
* @param valueExpiredTime 正常时间期望过期时间
* @param errorValueExpiredTime 数据接口查询错误过期时间
*/
public RedisCache(long valueExpiredTime, long nullValueExpiredTime, long errorValueExpiredTime) {
this.valueExpiredTime = valueExpiredTime;
this.nullValueExpiredTime = nullValueExpiredTime;
this.errorValueExpiredTime = errorValueExpiredTime;
}
/**
* 创建RedisCache实例 ,带有数据层逻辑回调实现,默认本地缓存大小为10万
*
* @param valueExpiredTime 空数据过期时间 ,时间单位为"秒"
* @param nullValueExpiredTime 正常时间期望过期时间,时间单位为"秒"
* @param errorValueExpiredTime 数据接口查询错误过期时间,时间单位为"秒"
* @param callback 数据层逻辑回调实现,实现自RedisCacheDataCallBack<K, V>接口
* 例子:
* RedisCache localCache = new RedisCache<Integer, List<Item>>(
* 60,//nullValueExpiredTime 空数据过期时间
* 60 * 60,//valueExpiredTime 正常时间期望过期时间
* 60,//errorValueExpiredTime 数据接口查询错误过期时间
* new RedisCacheDataCallBack<Integer, List<Item>>() {
* @Override public List<Item> getDataByKey(Integer key) {
* return getTopItemsImpl(key); //这里为业务逻辑实现
* }
* }
* );
*/
public RedisCache(long valueExpiredTime, long nullValueExpiredTime,
long errorValueExpiredTime, RedisCacheDataCallBack<K, V> callback) {
if (callback == null) {
throw new IllegalArgumentException("when create a new RedisCache, please implmement the RedisCacheDataCallBack first!");
}
this.callback = callback;
this.nullValueExpiredTime = nullValueExpiredTime;
this.valueExpiredTime = valueExpiredTime;
this.errorValueExpiredTime = errorValueExpiredTime;
}
/**
* 获取默认的RedisCache,过期时间默认为都为10s , 默认本地缓存大小为10万
*
* @param redisCacheDataCallBack 数据层逻辑回调实现,实现自RedisCacheDataCallBack<K, V>接口
* @return RedisCache 默认的实例
*/
public RedisCache<K, V> getDefaultRedisCache(RedisCacheDataCallBack<K, V> redisCacheDataCallBack) {
if (redisCacheDataCallBack == null) {
throw new IllegalArgumentException("when create a new RedisCache, please implmement the RedisCacheDataCallBack first!");
}
this.callback = redisCacheDataCallBack;
return new RedisCache<K, V>(nullValueExpiredTime, valueExpiredTime, errorValueExpiredTime, redisCacheDataCallBack);
}
/**
* 通过key从redis中获取对应的Value l
*
* @param key <K>
* @return V value
*/
public V get(String key, K k) {
return get(key,k,valueExpiredTime);
}
public V get(String key, K k, long expireTime) {
V result = (V) redisUtils.get(key);
if (result != null) {
return result;
}
// -> 没有数据,或者为NULL(NULL数据已经过期)
log.debug("没有数据,或者为NULL(NULL数据已经过期)");
try {
// 先调用数据查询接口(下层Memcache和DB的查询逻辑)
V callBackResult = callback.getRedisDataByKey(k);
if (callBackResult == null) {
// 没有数据
log.debug("5-无数据");
put(key, callBackResult, this.nullValueExpiredTime);
return null;
} else {
// 有数据,填充数据并设定时间(业务数据过期时间)
log.debug("6-有数据,填充数据并设定时间(业务数据过期时间)");
put(key, callBackResult, expireTime);
return callBackResult;
}
} catch (RedisCacheDataCallBackException e) {
e.printStackTrace();
// 数据查询报错,向Cache中回写Null,并设定过期时间(这里的时间可能偏短)
log.debug("7-数据查询报错");
putNULL(key, this.nullValueExpiredTime);
return null;
}
}
/**
* 向localcache中放入Null值
*
* @param key cache的Key
* @param expiredTime Null 值的过期时间
*/
private void putNULL(String key, long expiredTime) {
redisTemplate.opsForValue().set(key, null, expiredTime);
}
/**
* 向Cache中放入key和Value,需要指定过期时间
*
* @param key
* @param value
* @param expiredTime
*/
private void put(String key, V value, long expiredTime) {
redisUtils.set(key, value, expiredTime);
}
/**
* 注入对应的业务回调实例
*
* @param callback 数据层逻辑回调实现,实现自RedisCacheDataCallBack<K, V>接口
*/
public void setCallback(RedisCacheDataCallBack<K, V> callback) {
if (callback == null) {
throw new IllegalArgumentException(
"the callback is null here , please check !");
}
this.callback = callback;
}
/**
*
* @param key
* @param value
* @param expiredTime
*/
public int updateDbAndRedis(String key, V value, long expiredTime) {
//先存db,再删缓存
int result = callback.updateDbFromRedis(value);
redisUtils.delete(key);
//思考:为什么后通知,先更新DB,后更新缓存
//1.先更新DB,后删缓存:
//正常思考:并发查询时,查出的是redis的旧值,产生了1次脏读,但减少对DB的冲击。
//深度思考:此脏读是一时的旧数据,因随后会删redis,代价小,1次脏读。
//2.先删缓存,后更新DB:
//正常思考:并发查询时,因没有缓存,去数据库查询,对其冲击大。好处是可以查到最新的数据,但!
//深度思考:但查到实际不是最新的数据,因为还没有更新DB,会把不是最新的数据会放到redis里,导致长时间脏读,概率大,代价大。
// FutureTask future = new FutureTask(() -> {
//
// //推缓存
// Log.info("redis异步更新数据库", "key:{} data:{}", key, value);
// if (!redisUtils.hasKey(key)) {
// try {
//
// Field idField;
// PropertyDescriptor pd;
//
// if (value.getClass().getName().endsWith("WithBLOBs")) {
// idField = value.getClass().getSuperclass().getDeclaredField("id");
// pd = new PropertyDescriptor(idField.getName(), value.getClass().getSuperclass());
// } else {
// idField = value.getClass().getDeclaredField("id");
// pd = new PropertyDescriptor(idField.getName(), value.getClass());
// }
//
// Method getMethod = pd.getReadMethod();
// Long id = (Long) getMethod.invoke(value);
//
// if (id == null) {
// return null;
// }
// V v = callback.getById(id);
// put(key, v, expiredTime);
// } catch (NoSuchFieldException e) {
// log.error("id 不存在! 或者 调用 getById 失败", e);
// } catch (IllegalAccessException e) {
// log.error("redis异步更新数据库", e);
// } catch (Exception e) {
// log.error("redis异步更新数据库", e);
// }
// }
// return null;
// });
// executor.execute(future);
return result;
}
/**
*
* @param key
* @param value
*/
public int updateDbAndRedis(String key, V value) {
return this.updateDbAndRedis(key,value,valueExpiredTime);
}
public int insertDbAndRedis(String cacheConsants,V value, long expiredTime){
int primaryKey = callback.insertDbFromRedis(value);
// FutureTask<Void> future = new FutureTask(new Callable<Void>() {
// public Void call() {
// //推缓存
// Log.info("redis异步更新数据库","{key}:{data}",cacheConsants + primaryKey, value);
// if (!redisUtils.hasKey(cacheConsants + primaryKey)) {
// put(cacheConsants + primaryKey, value, expiredTime);
// }
// return null;
// }
// });
// executor.execute(future);
return primaryKey;
}
public int insertDbAndRedis(String key,V value){
return this.insertDbAndRedis(key,value,valueExpiredTime);
}
public static void main(String[] args) {
System.out.println("1");
FutureTask<Void> future = new FutureTask(new Callable<Void>() {
public Void call() {
//Thread.sleep(5);
System.out.println("线程执行");
return null;
}
});
executor.execute(future);
System.out.println("2");
}
}
分享到:
相关推荐
redis本地缓存与redis缓存
redis缓存 redis缓存
实现Redis分布式缓存,此文件为相关原理
redis分布式缓存+spring整合及 集群、分片等配置使用例子
redis页面缓存html使用redis实现页面缓存.docx
redis分布式缓存中间件培训PPT
redis多级缓存搭建资料
redis缓存服务器
SSM架构集成Redis做缓存,里面是一个完整的框架可以运行,sql文件在里面直接创建数据库
memcache&redis构建缓存服务pdf文档
shiro-demo使用redis做缓存.zip
springmvc整合Mybatis,Redis;实现将查询的数据进行二级缓存处理
基于SpringBoot、Redis、Mybatis的数据缓存,实现redis缓存mysql数据库的数据,并添加ssl证书,实现https访问!
独立缓存服务器: LinuxCentOS Redis版本: 3.0 下面我们针对于Redis安装做下详细的记录: 编译和安装所需的包: #yum install gcc tcl创建安装目录: #mkdir /usr/local/redis
什么是redis缓存穿透 雪崩 ,如何应对解决 redis缓存穿透 雪崩 的解决办法 redis缓存穿透的解决办法 redis雪崩的解决办法
想要做一个基于redis的缓存 比 spring cache好用 但是这个只局限于redis 有兴趣的大家一起来研究学习 代码还没有写完
spark一个Linux下使用C++学习编写的并发服务器,使用redis作为缓存和MySQL作为数据库,并利用GoogleTest编写单元测试和webbench进行压力测试。.zipspark一个Linux下使用C++学习编写的并发服务器,使用redis作为缓存...
tomcat8 通过 redis共享缓存
redis缓存的大致介绍.
micro-cache,基于redis实现的缓存模块。 标签:micro redis