建设银行客户端官方网站,成都关键词优化服务,潍坊个人做网站,wordpress contact基于Redis加锁注解AOP解决JOB重复执行问题 现象解决方案自定义注解定义AOP策略redis 加锁实践 现象
线上xxljob有时候会遇到同一个任务在调度的时候重复执行#xff0c;如下图#xff1a;
线上JOB服务运行了2个实例#xff0c;有时候会重复调度到同一个实例#xff0c;有… 基于Redis加锁注解AOP解决JOB重复执行问题 现象解决方案自定义注解定义AOP策略redis 加锁实践 现象
线上xxljob有时候会遇到同一个任务在调度的时候重复执行如下图
线上JOB服务运行了2个实例有时候会重复调度到同一个实例有时候会重复调度到不同实例上对于Job重复执行会存在很多风险可以采用Redis加锁的方式来解决。这里用统一的方式提供这个内部功能其他Job或者从管理页面进来的请求直接执行Job可以都限制住保证同一时间分布式环境中只有一个实例在运行。
解决方案
自定义注解
首先定义一个自定义注解将redis加锁需要的参数可以通过注解声明
Target({ElementType.METHOD})
Retention(RetentionPolicy.RUNTIME)
public interface JobNoRepete {
String name();
String redisKey();
long expireTime();
TimeUnit timeUnit();
}定义AOP策略
Component
Aspect
Slf4j
public class JobNoRepeteAop {
Resourceprivate RedisService redisService;
Around(value annotation(annotation), argNames pj,annotation)public Object around(ProceedingJoinPoint pj, JobRepetitionDefense annotation) throws Throwable {String name annotation.name();String redisKey annotation.redisKey();long expireTime annotation.expireTime();TimeUnit timeUnit annotation.timeUnit();log.info(job执行防重开始执行,name{},redisKey{},expireTime{},timeUnit{},name, redisKey, expireTime, timeUnit);try {return redisService.executeOnlyOnce(redisKey, expireTime, timeUnit, pj::proceed);} finally {log.info(job执行防重执行完成,name{}, name);}}}redis 加锁
redis 加锁逻辑使用spring redis中的StringRedisTemplate
Slf4j
Component
public class RedisService {Resourceprivate StringRedisTemplate stringRedisTemplate;public T T executeOnlyOnce(String redisKey, long expireTime, TimeUnit timeUnit, CustomCallableT callable) throws Throwable {if (StrUtil.isBlank(redisKey) || expireTime 0 || Objects.isNull(timeUnit) || Objects.isNull(callable)) {throw new IllegalArgumentException(参数错误);}String uuid UUID.randomUUID().toString();if (!stringRedisTemplate.opsForValue().setIfAbsent(redisKey, uuid, expireTime, timeUnit)) {throw new RuntimeException(任务正在执行,请稍后再试);}//执行逻辑try {return callable.call();} finally {//执行完成主动释放锁try {String oldValue stringRedisTemplate.opsForValue().get(redisKey);if (Objects.equals(uuid, oldValue)) {stringRedisTemplate.delete(redisKey);}} catch (Exception e) {//释放锁失败等待expireTime后自动释放log.error(释放锁异常, e);}}}
}
public interface CustomCallableV {V call() throws Throwable;
}实践
对于适用的场景就可以直接使用注解的方式进行声明例如
Service
Slf4j
public class testService {
private static final int EXPIRE_HOURS 24;
JobNoRepete(name 测试redis, redisKey Constant.JOB_LOCK_TO_REDIS,expireTime EXPIRE_HOURS, timeUnit TimeUnit.HOURS)public void test(LocalDate localDate) {//内部逻辑}
}