redis被动缓存

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);

//        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");
    }
}


猜你喜欢

转载自572327713.iteye.com/blog/2415575