public abstract class AsyncBaseModel {
    protected int lastUpdateTime;
    public int getLastUpdateTime() {
        return lastUpdateTime;
    }
    public void setLastUpdateTime(int lastUpdateTime) {
        this.lastUpdateTime = lastUpdateTime;
    }
    public abstract byte[] toBytes() ;
    public boolean equalsValue(AsyncBaseModel model) {
        return false;
    }
}
 
package com.tianxiawuxue.service.async;
import com.tianxiawuxue.mybatis.model.AsyncBaseModel;
import com.tianxiawuxue.utils.RedisUtils;
import com.tianxiawuxue.vo.Triple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import javax.annotation.Resource;
import java.util.concurrent.ThreadPoolExecutor;
/**
 * @author 天下武学
 * @param <P>
 * @param <V>
 * @param <E>
 */
public abstract class AbstractAsyncVersionService<P, V extends AsyncBaseModel, E extends SerializableMessagePack> {
    private static final Logger log = LoggerFactory.getLogger(AbstractAsyncVersionService.class);
    @Autowired
    public RedisUtils redisUtils;
    private enum ModelAsyncStatus {
        /**
         * 可以直接使用
         */
        STATUE_VALID,
        /**
         * 可以使用,直接返回。会异步触发进入队列
         */
        STATUE_VALID_ASYNC,
        /**
         * 不可以使用,需要直接走db,保存至redis,再返回
         */
        STATUE_INVALID
    }
    /**
     * redis中的数据的最大有效期
     */
    private static final int DEFAULT_VALUE_VALID_TIME_SECOND = 60 * 60 * 24 * 3;
    /**
     * redis中数据的,最小异步进queue的时间
     */
    private static final int DEFAULT_ASYNC_TIME_SECOND = 10 * 60;
    /**
     * redis的过期时间
     */
    private static final int DEFAULT_VALUE_EXPIRE_TIME_SECOND = DEFAULT_VALUE_VALID_TIME_SECOND;
    /**
     * 异步更新,最小间隔触发时间
     */
    private static final int DEFAULT_ASYNC_MIN_TRIGGER_TIME_SECOND = 10;
    /**
     * redis ext的过期时间
     */
    private static final int DEFAULT_EXT_EXPIRE_TIME_SECOND = 60 * 60 * 24 * 30 * 3;
    @Resource
    protected RedisTemplate<String, Object> redisTemplate;
    protected V getVersion(P p) {
        V t = getVersionByCache(p);
        ModelAsyncStatus status = getAsyncStatus(t);
        if (t == null || status == ModelAsyncStatus.STATUE_INVALID) {//redis木有数据,或者已经过期了
            t = getVersionByDbSynchronized(p);//防并发访问db
        } else if (status == ModelAsyncStatus.STATUE_VALID_ASYNC) {//redis有数据,且还未过期中
            async2QueueForUpdate2Redis(p);//给队列发一份需要更新的数据
        }
        return t;
    }
    protected E getExt(P p) {
        E e = getExtByCache(p);
        if (e != null) {
            return e;
        }
        Triple<V, E, V> triple = getExtByRemote(p, null, true);
        setVersionExtByCache(p, triple, true);
        return triple == null ? null : triple.getT2();
    }
    protected V getVersionByCache(P p) {
        String key = getValueKey(p);
        byte[] bytes = redisUtils.get(key);
        return newVersionInstance(bytes);
    }
    protected E getExtByCache(P p) {
        String key = getExtKey(p);
        byte[] bytes = redisUtils.get(key);
        return newExtInstance(bytes);
    }
    protected abstract V newVersionInstance(byte[] bytes);
    protected abstract E newExtInstance(byte[] bytes);
    protected abstract Triple<V, E, V> getExtByRemote(P p, E e, boolean isOptimisticLock);
    /**
     * <li>判断t.toBytes()的md5值与cache中的md5是否相等</li>
     * <li>如果相等,则t与cache中的值完全相同,则不需要更新cache</li>
     * <li>如果不等,则t.toBytes()需要覆盖cache值,且覆盖md5值</li>
     *
     * @param p     param
     * @param triple triple
     * @param isOptimisticLock true:乐观锁,false:非乐观锁
     * @return true:成功,false:失败
     */
    protected Boolean setVersionExtByCache(P p, Triple<V, E, V> triple, boolean isOptimisticLock) {
        if (triple == null) {
            log.info("setVersionByCache.triple null. error. p {}", p);
            return Boolean.FALSE;
        }
        final String key = getValueKey(p);
        final byte[] valueBytes = triple.getT1().toBytes();
        if (valueBytes == null) {
            log.info("setVersionByCache.valueBytes null. error. p {}", p);
            return Boolean.FALSE;
        }
        final V v = getVersionByCache(p);//当前的最新的redis值,如果有需求,改成lua版本的cas操作
        if(isOptimisticLock) { //如果是乐观锁,则需要cas写入数据
            final V v2 = triple.getT3();
            if(v2 == null) {
                log.info("setVersionExtByCache.isOptimisticLock is not init. error. p{}", p);
//                return Boolean.FALSE;
            }
            if(v2 != null && !v2.equalsValue(v)) { //即版本变化了,cas失败了.处理方案:放弃本轮更新
                return Boolean.FALSE;
            }
        }
        //如果版本号相等,则忽略值的修改
        if (!triple.getT1().equalsValue(v)) {
            final String extKey = getExtKey(p);
            final byte[] extBytes = triple.getT2().toBytes();
            if (extBytes == null) {
                log.info("setVersionByCache.extBytes null. error. p {}", p);
                return Boolean.FALSE;
            }
            log.info("AbstractAsyncVersionService.setVersionByCache.extBytes.done. p {}", p);
            redisTemplate.execute((RedisCallback<Boolean>) connection -> {
                connection.setEx(extKey.getBytes(), getExtExpireTimeSecond(), extBytes);
                return Boolean.TRUE;
            });
        }
        //版本无论是否发生了变化,都需要更新版本的cache-> lastUpdateTime
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            connection.setEx(key.getBytes(), getValueExpireTimeSecond(), valueBytes);
            return Boolean.TRUE;
        });
    }
    /**
     * 执行db操作,读取p对应的数据。
     * 同一时间仅一次db操作【参数p维度】
     */
    private V getVersionByDbSynchronized(P p) {
        log.info("AbstractAsyncVersionService.getVersionByDbSynchronized start " + p);
        String key = getSynchronizedKey(p);
        synchronized (key.intern()) {
            V v = getVersionByCache(p); //双检
            ModelAsyncStatus status = getAsyncStatus(v);
            if (status != ModelAsyncStatus.STATUE_VALID) {
                Triple<V, E, V> triple = getExtByRemote(p, null, true);
                setVersionExtByCache(p, triple, true);
                return triple == null ? null : triple.getT1();
            } else {
                log.info("AbstractAsyncVersionService.getVersionByDbSynchronized get by cache " + p);
            }
            return v;
        }
    }
    protected abstract ThreadPoolExecutor getThreadPoolExecutor();
    /**
     * ModelAsyncStatus.STATUE_VALID_ASYNC类型的数据,需要额外异步触发redis的更新【即:进入队列】
     */
    protected void async2QueueForUpdate2Redis(P p) {
        if (!getAndLockBeforeQueue(p)) {
            return;
        }
        getThreadPoolExecutor().execute(() -> {
            Boolean lock = getAndLockAsyncLock(p);
            //先抢一个分布式锁,失败则为已经更新了,且最多10s内一个storeId
            if (lock != null && lock) {
                long start = System.currentTimeMillis();
                Triple<V, E, V> triple = getExtByRemote(p, null, true);
                Boolean result = setVersionExtByCache(p, triple, true);
                if (result == null || !result) {
                    unLockAsyncLock(p);
                }
                log.info("AbstractAsyncVersionService#async2QueueForUpdate2Redis.lock.success param:{}, cost:{}", p, (System.currentTimeMillis() - start));
            } else {
                log.info("AbstractAsyncVersionService#async2QueueForUpdate2Redis.lock.fail param:{}", p);
            }
        });
    }
    /**
     * 进入队列前,前抢锁
     *
     * @param p p
     * @return true:抢锁成功,可以进入队列
     */
    protected abstract Boolean getAndLockBeforeQueue(P p);
    /**
     * 对参数P,在队列中最大频次的触发几率。
     *
     * @return true: 上锁成功
     */
    protected Boolean getAndLockAsyncLock(P p) {
        final String key = getRedisLockKey(p);
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            Boolean result = connection.setNX(key.getBytes(), new byte[]{(byte) 1});
            connection.expire(key.getBytes(), getAsyncMinTriggerTimeSecond());
            return result;
        });
    }
    /**
     * 队列任务触发异常,删除锁【基本上不会触发这个del操作】
     */
    protected void unLockAsyncLock(P p) {
        final String key = getRedisLockKey(p);
        redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            connection.del(key.getBytes());
            return Boolean.TRUE;
        });
    }
    /**
     * 获取redis的分布式锁 key
     * 上锁成功,则执行队列任务
     */
    protected abstract String getRedisLockKey(P p);
    /**
     * 存储数据的redis key
     */
    protected abstract String getValueKey(P p);
    /**
     * 存储扩展数据的redis key
     */
    protected abstract String getExtKey(P p);
    /**
     * 获取JVM内存锁Synchronization key
     * 单JVM同一时间仅触发一次db操作【参数p维度】
     */
    protected abstract String getSynchronizedKey(P p);
    /**
     * 判断对象t在自动lastUpdateTime修饰后的有效性
     */
    private ModelAsyncStatus getAsyncStatus(V v) {
        if (v == null) {
            return ModelAsyncStatus.STATUE_INVALID;
        }
        long time = (int) (System.currentTimeMillis() / 1000L) - v.getLastUpdateTime();
        return (time > getValueValidTimeSecond()) ? ModelAsyncStatus.STATUE_INVALID : (time > getAsyncTimeSecond() ? ModelAsyncStatus.STATUE_VALID_ASYNC : ModelAsyncStatus.STATUE_VALID);
    }
    private int getValueValidTimeSecond() {
        return DEFAULT_VALUE_VALID_TIME_SECOND;
    }
    private int getAsyncTimeSecond() {
        return DEFAULT_ASYNC_TIME_SECOND;
    }
    private int getValueExpireTimeSecond() {
        return DEFAULT_VALUE_EXPIRE_TIME_SECOND;
    }
    private int getExtExpireTimeSecond() {
        return DEFAULT_EXT_EXPIRE_TIME_SECOND;
    }
    private int getAsyncMinTriggerTimeSecond() {
        return DEFAULT_ASYNC_MIN_TRIGGER_TIME_SECOND;
    }
}