github地址:https://github.com/lukas-krecan/ShedLock
目前公共存儲目前支持的有:
- Monogo
- DynamoDB
- JdbcTemplate
- ZooKeeper (using Curator)
- Redis (using Spring RedisConnectionFactory)
- Redis (using Jedis)
- Hazelcast
這裏我用mysql做模擬:
pom文件:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- shedlock -->
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>8.0.15</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.28</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
</dependencies>
yml文件:
spring:
datasource:
name: ilabservice
url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8
username: root
password: 12345678
driver-class-name: com.mysql.jdbc.Driver
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
initialize: false
jpa:
hibernate:
show-sql: true
創建數據庫sql文件:
- MySQL
CREATE TABLE shedlock(
name VARCHAR(64),
lock_until TIMESTAMP(3) NULL,
locked_at TIMESTAMP(3) NULL,
locked_by VARCHAR(255),
PRIMARY KEY (name)
)
- SQLServer
CREATE TABLE [dbo].[shedlock](
[name] [varchar](64) NOT NULL,
[lock_until] [datetime] NULL,
[locked_at] [datetime] NULL,
[locked_by] [varchar](255) NOT NULL,
CONSTRAINT [PK_shedlock] PRIMARY KEY CLUSTERED
(
[name] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
- Oracle
CREATE TABLE SHEDLOCK (
name VARCHAR2(64 CHAR),
lock_until TIMESTAMP,
locked_at TIMESTAMP,
locked_by VARCHAR2(255 CHAR),
PRIMARY KEY (name)
);
配置類
import com.alibaba.druid.pool.DruidDataSource;
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import net.javacrumbs.shedlock.spring.ScheduledLockConfiguration;
import net.javacrumbs.shedlock.spring.ScheduledLockConfigurationBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.time.Duration;
@Component
public class ShedLockConfig {
@Value("${spring.datasource.url}")
private String dbUrl;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.initialSize}")
private int initialSize;
@Value("${spring.datasource.minIdle}")
private int minIdle;
@Value("${spring.datasource.maxActive}")
private int maxActive;
@Value("${spring.datasource.maxWait}")
private int maxWait;
@Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.testOnReturn}")
private boolean testOnReturn;
@Value("${spring.datasource.poolPreparedStatements}")
private boolean poolPreparedStatements;
@Value("${spring.datasource.filters}")
private String filters;
@Value("{spring.datasource.connectionProperties}")
private String connectionProperties;
@Bean
public LockProvider lockProvider(DataSource dataSource) {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(this.dbUrl);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName("com.mysql.jdbc.Driver");
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
datasource.setPoolPreparedStatements(poolPreparedStatements);
try {
datasource.setFilters(filters);
} catch (SQLException e) {
}
datasource.setConnectionProperties(connectionProperties);
return new JdbcTemplateLockProvider(datasource);
}
@Bean
public ScheduledLockConfiguration scheduledLockConfiguration(LockProvider lockProvider) {
return ScheduledLockConfigurationBuilder.withLockProvider(lockProvider)
.withPoolSize(10)
.withDefaultLockAtMostFor(Duration.ofMinutes(10))
.build();
}
}
主程序啓動類:
@EnableSchedulerLock,開啓定時任務鎖,指定一個默認的鎖的時間
@SpringBootApplication
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class ShedlockApplication {
public static void main(String[] args) {
SpringApplication.run(ShedlockApplication.class, args);
}
}
定時任務測試類:
import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.core.SchedulerLock;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class TestTask {
Integer count = 0;
Integer count2 = 0;
@Scheduled(cron = "* * * * * ?")
@SchedulerLock(name = "testJob1", lockAtLeastFor = 6000)
public void doTask() throws InterruptedException {
log.info(Thread.currentThread().getName() + "===task run" + "----" + count);
count++;
// Thread.sleep(2000);
Thread.sleep(10000);
log.info(Thread.currentThread().getName() + "===task end");
}
@SchedulerLock(name = "testJob2", lockAtLeastFor = 4000)
@Scheduled(initialDelay = 3000, fixedDelay = 1000)
public void doTask2() throws InterruptedException {
log.info(Thread.currentThread().getName() + "===task2 run" + "----" + count2);
Thread.sleep(2000);
count2++;
log.info(Thread.currentThread().getName() + "===task2 end");
}
}
@SchedulerLock的作用是保證當前定時任務的方法執行時獲得鎖,忽略其他相同任務的執行。但是,name必須要指定,ShedLock就是根據這個name進行相同任務判定的。註解中有五個個參數:
- lockAtLeastForString:鎖持有的最小時間(ms)。表示當前方法執行時獲取到的鎖的最小時間是6000ms,6000ms之內,就算當前定時任務已經執行完成,其他任務也無法獲得鎖,必須等下一秒之後才能獲取。
- lockAtMostForString:鎖持有的最大時間(ms)。假如當前任務獲取的鎖的最大持有時間爲30s,30s之後就算沒有執行完,其他定時任務也可以獲取鎖去執行任務。例如:PT30S 代表30秒
- name:定時任務的名字,就是數據庫中的內個主鍵
- lockAtMostFor:鎖的最大時間單位爲毫秒
- lockAtLeastFor:鎖的最小時間單位爲毫秒
分別啓動兩個實例,一個2000毫秒一個10000毫秒:
從輸出日誌中我們可以看到,兩個節點在交替執行任務,沒有重複執行。
來看一下表裏的數據: