前言

场景:现在集群越来越普及,一个服务拥有多个实例时,何如避免定时任务重复执行?


一、不使用自动生效的定时任务

详见:https://blog.csdn.net/qq_37700773/article/details/109385361?spm=1001.2014.3001.5501
但是不建议使用,原因是不方便。

二、使用redis 创建一个标识来判断(此处引用同事的想法)

定时任务启动的时候,先去redis查询是指定key是否存在值(这里使用reids 的AtomicLong)
这里使用的是redission,实质上执行的这句代码:

	// CacheUtil.incrAtomicLong(Key)这句代码的底层
    public long incrAtomicLong(String key) {
        return redissonClient.getAtomicLong(key).incrementAndGet();
    }

## 1.引入库

三、使用XXLJOB

xxljob是一个单独的服务,思路是:
1:业务系统内没有定时任务,而是将定时任务逻辑写成一个接口
2:在xxljob服务端管理台配置好参数,由xxljob服务定时调用业务系统
3:xxljob服务调用业务集群(xxljob请求网关/ng,最终请求哪个业务实例,由网关/ng规则决定)

xxljob这种模式,还是有点麻烦。而且我经常会忘记:还有xxljob?部署在哪?地址多少?


四、使用ShedLock + redis

推荐使用这个。对性能要求不是特别高的(性能要求在比肩某东、某宝的…这款肯定也不合适)
这个只是日常开发使用,执行任务的服务实例在十几个以内的,应该没啥问题。
推荐的主要原因是:很简单

spring项目中引入依赖:

        <!-- 分布式定时任务锁  shedlock支持 MongoRedisHazelcastZooKeeper,JDBC,这里使用redis -->
        <!-- https://mvnrepository.com/artifact/net.javacrumbs.shedlock/shedlock-spring -->
        <dependency>
            <groupId>net.javacrumbs.shedlock</groupId>
            <artifactId>shedlock-spring</artifactId>
            <version>4.0.4</version>
        </dependency>
        <!-- 使用redis做分布式任务 -->
        <dependency>
            <groupId>net.javacrumbs.shedlock</groupId>
            <artifactId>shedlock-provider-redis-spring</artifactId>
            <version>2.5.0</version>
        </dependency>

增加shedLock的配置,使用的redis

package com.tao.scheduled.shedLock;

import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider;
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
 
/**
 * 分布式定时调度配置
 * <p>
 * SchedulerLock 注解一共支持五个参数,分别是
 * name 用来标注一个定时服务的名字,被用于写入数据库作为区分不同服务的标识,如果有多个同名定时任务则同一时间点只有一个执行成功
 * lockAtMostFor 成功执行任务的节点所能拥有独占锁的最长时间,单位是毫秒ms
 * lockAtMostForString 成功执行任务的节点所能拥有的独占锁的最长时间的字符串表达,例如“PT14M”表示为14分钟
 * lockAtLeastFor 成功执行任务的节点所能拥有独占锁的最短时间,单位是毫秒ms
 * lockAtLeastForString 成功执行任务的节点所能拥有的独占锁的最短时间的字符串表达,例如“PT14M”表示为14分钟
 */
@Configuration
@EnableSchedulerLock(defaultLockAtMostFor = "PT30M")
public class ShedLockConfig {
 
    @Bean
    public LockProvider lockProvider(RedisTemplate redisTemplate) {
        return new RedisLockProvider(redisTemplate.getConnectionFactory());
    }
 
}

最简单的定时任务代码

package com.tao.scheduled.shedLock;

import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * @Describe 避免定时调度重复执行
 **/
@Slf4j
@Component
public class ShedLockDemo {

//    @Scheduled(cron = "0/2 * * * * ?")  每2秒执行一次
    @Scheduled(cron = "0/2 * * * * ?")
    @SchedulerLock(name = "scheduleDemo01")
    public void ScheduleDemo01() {
        log.info("开启定时任务......");

    }

}

启动类加上注:@EnableScheduling


//@EnableFeignClients//开启feign
@EnableSwagger2 //开启 Swagger
@SpringBootApplication
@EnableScheduling//开启定时任务
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class);
        System.out.println("======......项目启动成功......======"+ServerPortConfig.host+":"+ ServerPortConfig.serverPort);

    }
}

测试方法:
idea启动两个实例,直接观察日志…

最后说明

这篇文章,记录的是工作中遇到的问题,写的比较水。主要用于工作总结和记录

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐