在多线程处理业务中,容易出现数据重复执行,报错情况
redis 工具类:
放入lock ,以及过期时间
/**
* 普通缓存放入并设置时间
*
* @param key
* 键
* @param value
* 值
* @param time
* 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public static boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
log.error("set() is error : {}", e);
return false;
}
}
redisLock 工具类:
一定要有锁的持有者,不然在多线程下,就容易出现问题
/**
* 获取redis锁
*
* @param lockKey 锁标识 key
* @param requestId 锁的持有者,加锁的请求
* @param expireTime 锁过期时间
* @return
*/
public static boolean getLock(String lockKey, String requestId, int expireTime) {
// 设置过期时间为30秒
int expired = 30;
Object o = RedisUtil.get(lockKey);
if (o != null) {
return true;
} else {
// SET_WITH_EXPIRE_TIME 设置过期时间 时间由expireTime决定
RedisUtil.set(lockKey, requestId, expireTime);
return false;
}
}
/**
* 释放锁
*
* @param lockKey
* @param requestId
* @return
*/
public static void releaseLock(String lockKey, String requestId) {
// 方式1
if (RedisUtil.get(lockKey).equals(requestId)) { // 校验当前锁的持有人与但概念请求是否相同
// 执行在这里时,如果锁被其它请求重新获取到了,此时就不该删除了
RedisUtil.removeKey(lockKey);
// }
// 方式2
// eval() 方法会交给redis服务端执行,减少了从服务端再到客户端处理的过程
// 赋值 KEYS[1] = lockKey ARGV[1] = requestId
/* String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object releaseResult = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (RELEASE_RESULT.equals(releaseResult.toString())) {
return true;
}
return false;*/
}
}
public ExecuteResult doWork(JobContext executableContext) {
String lockName = RedisKeys.WINDOW_OS_SAVE_ODPS_TABLE_TO_MYSQL;
/**
*由于redis lock 没有把锁给到具体人,所以锁失效
*/
// boolean lock = RedisLock.getLock(lockName);
UUID uuid = UUID.randomUUID();
boolean getResult = RedisLock.getLock(lockName, uuid.toString(), 30);
if (getResult) {
log.info("[缓存新增表结构job]==> 当前数据源有线程正在操作,此次校验不通过");
String message = String.format("[jobId:%s,jobName:%s][缓存新增表结构job]==> 当前数据源有线程正在操作,此次校验不通过",jobInfo.getJobId(),jobInfo.getJobName());
return ExecuteResult.createSucceed(message);
}
try{
//业务代码
}catch (Exception e) {
String message = String.format("[jobId:%s,jobName:%s][缓存新增表结构job]==> 缓存ODPS新增表结构同步异常,Exception:%s",jobInfo.getJobId(),jobInfo.getJobName(),e);
return ExecuteResult.createSucceed(message);
} finally {
// RedisLock.unLock(lockName);
RedisLock.releaseLock(lockName, uuid.toString());
}
String message = String.format("[jobId:%s,jobName:%s]执行处理同步Mysql新增表结构数据至MysqlDB中的job成功!", jobInfo.getJobId(), jobInfo.getJobName());
return ExecuteResult.createSucceed(message);
}