一般在springcloud下单机执行定时任务的代码

@EnableScheduling
public class TestTask {

    @Scheduled(cron = "0 * * * * ?")
    public void test() {
        System.out.println("say hello");
    }
}

但因为微服务下一般是多实例部署,会导致定时任务多个实例同时执行的情况。
通过对EnableScheduling进行分析,在调用定时方法时会把Method封装成Runnable执行,并且createRunnable可以重写。
因此,只要在Runnable运行时加分布式锁,保证只有一个实例可以获取到锁并执行任务。

@Slf4j
@Configuration
public class SchedulingConfig {

    @Autowired
    private RedissonClient redissonClient;

    @Bean
    public ScheduledTaskRegistry scheduledTaskRegistry() {
        return new ScheduledTaskRegistry(redissonClient);
    }

    public static class ScheduledTaskRegistry extends ScheduledAnnotationBeanPostProcessor {

        private RedissonClient redissonClient;

        public ScheduledTaskRegistry(RedissonClient redissonClient) {
            this.redissonClient = redissonClient;
        }

        public ScheduledTaskRegistry(ScheduledTaskRegistrar registrar, RedissonClient redissonClient) {
            super(registrar);
            this.redissonClient = redissonClient;
        }

        @Override
        protected Runnable createRunnable(Object target, Method method) {
            String redisKey = RedisKeyUtil.getKey("stask", method.getDeclaringClass().getName() + "." + method.getName());
            RLock lock = redissonClient.getLock(redisKey);
            Runnable runnable = super.createRunnable(target, method);
            return () -> {
                if (!lock.tryLock()) {
                    log.info("运行中: {}", redisKey);
                    return;
                }
                log.info("运行: {}", redisKey);
                try {
                    runnable.run();
                } finally {
                    lock.unlock();
                }
            };
        }
    }
}

延伸

也可以通过redis分布式锁实现简单的选举leader功能。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐