ShedLock做定时任务锁

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毫秒:

从输出日志中我们可以看到,两个节点在交替执行任务,没有重复执行。

来看一下表里的数据:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章