分布式定时任务有好几种实现,我这里用了mysql和redis来实现
我们在项目中经常要用到定时任务,当有天服务器压力过大,我们就会增加服务器的数量,这时每台服务器的定时任务都在同一时间运行,导致我们需要同一时间运行一次的定时任务跑了多次,这时就需要解决这个问题,就用到了 shedlock
下面这是我项目里写的定时任务,里面的方法没有写出来
@Component
public class TimeOutSMSScheduledLock {
@Autowired
public TimeOutSMSService timeOutSMSService;
//这时定时任务
@Scheduled(cron = "0 0 9,14 * * ?")
public void sendSMS(){
//具体的方法我就不写了,你可以随便写也可以打印日志
timeOutSMSService.timeOutSendSMS();
}
}
一、mysql实现分布式定时任务锁
依赖:
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>2.2.0</version>
</dependency>
配置,创建一个它要用到的bean
@Configuration
public class ScheduledLockConfig {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
}
然后在数据库建一张表
CREATE TABLE `shedlock` (
`name` varchar(64) NOT NULL DEFAULT '',
`lock_until` timestamp NULL DEFAULT NULL,
`locked_at` timestamp NULL DEFAULT NULL,
`locked_by` varchar(255) DEFAULT NULL,
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
里面有四个字段:
- 主键name:每个定时任务的一个名字
- locked_at:锁的开始时间
- lock_until:锁的结束时间
再定时开始时,会更新这两个时间,在时间之内的定时是不会被执行的
在启动类上加@EnableSchedulerLock注解
@SpringBootApplication
//启用自带定时任务
@EnableScheduling
//定时任务锁
//defaultLockAtMostFor 指定在执行节点结束时应保留锁的默认时间使用ISO8601 Duration格式
//作用就是在被加锁的节点挂了时,无法释放锁,造成其他节点无法进行下一任务
//这里默认30s
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class CmccVoicesApiApplication {
public static void main(String[] args) {
SpringApplication.run(CmccVoicesApiApplication.class, args);
}
}
在方法上加@SchedulerLock注解
@Component
public class TimeOutSMSScheduledLock {
@Autowired
public TimeOutSMSService timeOutSMSService;
//这是定时任务
@Scheduled(cron = "0 0 9,14 * * ?")
//参数自己根据情况设置
@SchedulerLock(name = "sugExpiredSMS", lockAtMostFor = 1000*60*60*10, lockAtLeastFor = 1000*60*60*5)
public void sendSMS() {
//具体的方法我就不写了,你可以随便写也可以打印日志
timeOutSMSService.timeOutSendSMS();
}
}
- name:定时任务的名字,就是数据库中的内个主键
- lockAtMostFor:锁的最大时间单位为毫秒
- lockAtMostForString:最大时间的字符串形式,例如:PT30S 代表30秒
- lockAtLeastFor:锁的最小时间单位为毫秒
- lockAtLeastForString:最小时间的字符串形式
这样mysql的分布式定时任务锁就完成了
二、redis 实现分布式定时任务锁
redis其实和mysql类似,只是配置和导包的不同
依赖
<!-- 分布式定时任务锁 -->
<!-- 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
@Configuration
@EnableSchedulerLock(defaultLockAtMostFor = "PT30M")
public class ShedLockConfig {
@Bean
public LockProvider lockProvider(RedisTemplate redisTemplate) {
return new RedisLockProvider(redisTemplate.getConnectionFactory());
}
}
在启动类上添加注解
@SpringBootApplication
//这个是定时任务,必须要有
@EnableScheduling
public class AppsApplication {
public static void main(String[] args) {
SpringApplication.run(AppsApplication .class, args);
}
}
在定时任务上加注解@SchedulerLock,这里和mysql是一样
@Component
public class TimeOutSMSScheduledLock {
@Autowired
public TimeOutSMSService timeOutSMSService;
//这是定时任务
@Scheduled(cron = "0 0 9,14 * * ?")
//参数自己根据情况设置
@SchedulerLock(name = "sugExpiredSMS", lockAtMostFor = 1000*60*60*10, lockAtLeastFor = 1000*60*60*5)
public void sendSMS() {
//具体的方法我就不写了,你可以随便写也可以打印日志
timeOutSMSService.timeOutSendSMS();
}
}
这样redis的分布式定时任务锁完成