• 解决XXLJOB重复执行问题--Redis加锁+注解+AOP


    基于Redis加锁+注解+AOP解决JOB重复执行问题

    现象

    线上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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    定义AOP策略

    @Component
    @Aspect
    @Slf4j
    public class JobNoRepeteAop {
    
        @Resource
        private 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);
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    redis 加锁

    redis 加锁逻辑,使用spring redis中的StringRedisTemplate:

    @Slf4j
    @Component
    public class RedisService {
    
        @Resource
        private StringRedisTemplate stringRedisTemplate;
    
        public <T> T executeOnlyOnce(String redisKey, long expireTime, TimeUnit timeUnit, CustomCallable<T> 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);
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    public interface CustomCallable<V> {
        V call() throws Throwable;
    }
    
    • 1
    • 2
    • 3

    实践

    对于适用的场景就可以直接使用注解的方式进行声明,例如:

    @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) {
             //内部逻辑
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    JavaWeb总结:
    目标检测算法——遥感影像数据集资源汇总(附下载链接)
    云计算安全的新挑战:零信任架构的应用
    python+windows画图工具--复现别人论文中的colormap 方法2
    女鹅冬天的第一件羽绒服,当然要时尚经典的
    R语言、02 案例2-1 Pelican商店、《商务与经济统计》案例题
    Android调用浏览器打开指定页面
    walking机器人仿真教程-应用-使用直线路径规划器插件实现导航
    【语音之家】AI产业沙龙—语音技术在国音智能的应用
    阿里云4核16G服务器咋收费?26元1个月、149元半年
  • 原文地址:https://blog.csdn.net/renmengmeng520/article/details/133946347