集群,如果没有锁,就会同时跑同一个定时,浪费资源不说,还容易出bug,如下所示
现在ShedLock可以解决这个问题
1导包
1)pom文件
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- shedlock -->
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>2.5.0</version>
</dependency>
2)yaml 依赖mysql
spring:
datasource:
url: jdbc:mysql://localhost:3306/basedemo?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai
username: root
password: '123'
2.启动类打开 @EnableSchedulerLock(defaultLockAtMostFor = "PT10S")
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT10S")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
配置类
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class ScheduledLockConfig {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
}
建表sql
CREATE TABLE shedlock(
name VARCHAR(64),
lock_until TIMESTAMP(3) NULL,
locked_at TIMESTAMP(3) NULL,
locked_by VARCHAR(255),
PRIMARY KEY (name)
)
3.例子
import net.javacrumbs.shedlock.core.SchedulerLock;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class SchedulTest {
public static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static final int lockTime = 1000;
@Scheduled(cron = "0/10 * * * * ?")
@SchedulerLock(name = "TaskScheduler_scheduledTask", lockAtMostFor = lockTime, lockAtLeastFor = lockTime)
public void reportCurrentTime() {
System.out.println("现在时间是" + format.format(new Date())+ Thread.currentThread().getName());
}
}
如下图所示,每次只跑一个定时
4.下面详细解释以下配置
@SchedulerLock注释有几个用途。首先,只有带注释的方法被锁定,库会忽略所有其他计划的任务。您还必须为锁指定名称。同一时间只能执行一个具有相同名称的任务。
您还可以设置lockAtMostFor属性,该属性指定在执行节点死亡时锁应该保留多长时间。这只是一个回退,在正常情况下,锁会在任务完成时释放。您必须将lockAtMostFor设置为一个比正常执行时间长得多的值。如果任务花费的时间超过lockAtMostFor,结果的行为可能是不可预测的(更多的进程将有效地持有锁)。
最后,您可以设置lockAtLeastFor属性,该属性指定锁应该保存的最短时间。它的主要目的是防止多个节点执行非常短的任务和节点之间的时钟差异。
通过设置lockAtMostFor,我们确保即使节点死亡,锁也被释放;通过设置lockAtLeastFor,我们确保它不会在15分钟内执行超过一次。请注意,对于执行任务的节点死亡的情况,lockAtMostFor只是一个安全网,因此将它设置为一个比最大估计执行时间大得多的时间。如果任务花费的时间比lockAtMostFor更长,则可能再次执行该任务,结果将是不可预测的(更多进程将持有锁)。