跳至主要內容

ShedLock-定时任务锁

言午日尧耳总大约 3 分钟Java微服务SpringCloud分布式

ShedLock-定时任务锁

设置

配置

  • 这里演示使用Jdbc,支持Mysql、Postgres等关系型数据库
  • 还可以支持Mongo、Redis等存储方案,依赖包查询官网
    <properties>
        <!-- JDK>17 && Spring 6 使用5.x以上 ,旧版本 使用4.x -->
        <shedlock.version>5.10.2</shedlock.version>
    </properties>

    <dependencies>
      
        <dependency>
            <groupId>net.javacrumbs.shedlock</groupId>
            <artifactId>shedlock-spring</artifactId>
            <version>${shedlock.version}</version>
        </dependency>
        <!-- 配套锁表,演示使用jdbc,还支持Mongo、Redis等存储方案 -->
        <dependency>
            <groupId>net.javacrumbs.shedlock</groupId>
            <artifactId>shedlock-provider-jdbc-template</artifactId>
            <version>${shedlock.version}</version>
        </dependency>
      
      <dependencies>
  • 入口增加 @EnableSchedulerLock注解
  • defaultLockAtMostFor:默认最长锁定时间
    • 用于异常情况,如:执行过程中节点挂掉,未调用锁释放方法,该时长达到会强制释放锁
    • 如果执行时间小于该时长,执行完成会自动释放锁
  • defaultLockAtLeastFor:默认最短锁定时长
    • 用于避免执行时长过短,如下情况:
      • 两个容器调用间隔100ms,执行时间是50ms
      • 第一个容器获得锁,50ms执行完成,释放锁
      • 第二个容器在100ms时成功获得锁(已第一个容器被释放),执行程序
      • 因此两个容器重复执行了
    • 该参数可避免以上情况,设置1分钟(1m)、10分钟(10m)等
@Configuration
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "1m", defaultLockAtLeastFor = "1m")
class Application{
    ...
}

数据库配置

  • 数据库创建表
-- MySql
CREATE TABLE IF NOT EXISTS shedlock(
    name VARCHAR(64) NOT NULL,
    lock_until TIMESTAMP(3) NOT NULL,
    locked_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
    locked_by VARCHAR(255) NOT NULL,
    PRIMARY KEY (name)
);

---------------分割线,只需要选择一种
-- 官网介绍说明
# MySQL, MariaDB
CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until TIMESTAMP(3) NOT NULL,
    locked_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name));

# Postgres
CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until TIMESTAMP NOT NULL,
    locked_at TIMESTAMP NOT NULL, locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name));

# Oracle
CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until TIMESTAMP(3) NOT NULL,
    locked_at TIMESTAMP(3) NOT NULL, locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name));

# MS SQL
CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until datetime2 NOT NULL,
    locked_at datetime2 NOT NULL, locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name));

# DB2
CREATE TABLE shedlock(name VARCHAR(64) NOT NULL PRIMARY KEY, lock_until TIMESTAMP NOT NULL,
    locked_at TIMESTAMP NOT NULL, locked_by VARCHAR(255) NOT NULL);
  • jdbc配置
    • 需要上面的jdbc配套锁表依赖
@Configuration
public class ShedLockConfig {
    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(
                JdbcTemplateLockProvider.Configuration.builder()
                        .withJdbcTemplate(new JdbcTemplate(dataSource))
                        // 使用数据库时区
                        // .usingDbTime()

                        // 自定义时区
                        .withTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()))
                        .build()
        );
    }
}

使用

  • 使用@SchedulerLock注解任务
  • name: 名称id,用来识别任务
  • lockAtMostFor:最长锁定时间(不填则使用入口类的设置的默认值)
  • lockAtLeastFor:最短锁定时长(不填则使用入口类的设置的默认值)
import net.javacrumbs.shedlock.core.SchedulerLock;

@Slf4j
@Component
public class SchedulerTask {
    // Spring定时任务
    @Scheduled(cron = "*/15 * * * * *")
    // 锁配置
    @SchedulerLock(name = "scheduledTaskName", lockAtMostFor = "10m", lockAtLeastFor = "1m")
    public void scheduledTask() {
        // do something
        log.info("执行,时间:{}", LocalDateTime.now());
    }
}
  • 约一分钟执行一次
    • 虽然定时是每15秒执行一次,但是锁表是1分钟
    • 所以后面几次执行由于锁表跳过了
  • 定时任务的15秒是按时钟,而锁表时长是执行代码开始计算,可能已消耗了几毫秒,所以有可能第1分钟的时候依然在锁表状态(差几毫秒),导致在1分15秒时才执行第二次
  • 数据表生成的数据如下: | | lock_untill | locked_at | locked_by | | --- | --- | --- | --- | | scheduledTaskName | 2024-01-01 00:01:00.000 | 2024-01-01 00:00:00.000 | xxc-computer |
上次编辑于:
贡献者: 许晓聪