首先说一下场景,不根据实际场景讲的技术都是吹流弊,没人反对吧,咳咳
医院挂号,需要尽量高效的显示最新的数据,根据不同的科室设置不同的缓存时间,因为科室的热门程度也不一样嘛,这里本次只分享我学习的一些心得.
思路:号源的缓存是30分钟,然后在第25的时候,如果还有患者访问某个部门的号源,就开启一条异步线程直接查询最新的号源,重新放入缓存中.如果没有,就直接让缓存失效,这样就区别出来了热点科室和冷门科室
工具:思路就是自定义一个key,当前线程的name存为value,然后设置过期时间,调用 distributeLock 方法加锁,调用 releaseLock释放锁. 其中加锁的思路其实就是redis的setnx命令,每个线程都尝试加锁,因为 setnx就是一个原子性的操作,只有一个线程可以设置成功
/**
* 异步重新号源信息
* @param queryParms
*/
public void asynReservationsByDoctorsByThread(final QueryParms queryParms){
final String lockKey = new String("lock#co#asynReservationsByDoctorsByThread");
final String clientId = Thread.currentThread().getName();
boolean lockFlag = distributeLock(lockKey, clientId, JIUYUAN_LOCK_TIME);
if (lockFlag){
new Thread(new Runnable() {
@Override
public void run() {
logger.info("开始一个新的线程加载最新号源!_____star");
try {
for (String doctorId : queryParms.getDoctorIds()) {
if (queryParms.getConverted()) {
queryParms.setMyDoctorId(doctorId);
} else {
queryParms.setDoctorId(doctorId);
}
logger.info("getReservationsByDoctors queryParms is =>" + queryParms);
List l = getReservationsByDoctorIdDays(queryParms);
}
}catch (BusinessException e){
throw new BusinessException("异步加载信息异常:" + e.getMessage() + "请求参数:" + queryParms);
}finally {
releaseLock(lockKey, clientId);
logger.info("开始一个新的线程加载最新号源!___end");
}
}
}).start();
};
}
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";//毫秒 如果是秒的话就是EX
/**
* 这是一个分布式锁,用于获取key锁,如果抢占成功,并强行设置当前锁的过期时间
*
* @param key
* @param value
* @param seconds
* @return
*/
public boolean distributeLock(final String key, final String value, final long seconds) {
return (Boolean)redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
Jedis jedis = (Jedis) connection.getNativeConnection();
String result = jedis.set(key, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, seconds);
if (LOCK_SUCCESS.equals(result)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
});
}
//Lua脚本
private static final String RELEASE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
/**
* 调用Lua脚本释放锁
* @param key
* @param clientId
* @return
*/
public boolean releaseLock(final String key, final String clientId) {
return redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {
Jedis jedis = (Jedis) redisConnection.getNativeConnection();
Object result = jedis.eval(RELEASE_LOCK_SCRIPT, Collections.singletonList(key), Collections.singletonList(clientId));
if (RELEASE_SUCCESS.equals(result)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
});
}