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