springboot基础(82):分布式定时任务解决方案shedlock

文章目录

前言

多节点或者多服务器拥有相同的定时任务,这种情况下,不同节点的相同定时任务会被重复执行。如何解决分布式定时任务重复执行问题?此刻我们可以引入分布式定时任务解决shedlock来解决这种定时任务重复执行的问题。

简介

Shedlock是一个基于Java的分布式锁库,用于解决分布式环境下的并发问题。它可以确保同一时间只有一个线程能够获取到锁,从而避免了多线程竞争导致的数据不一致或错误的问题。

Shedlock的原理是在数据库中创建一个特殊的表,用于记录锁的状态和持有者信息。当一个线程想要获取锁时,它会在表中插入一条记录,如果插入成功,则表示该线程成功获取到了锁;否则,表示有其他线程已经获取到了锁,当前线程需要等待。

Shedlock提供了简单易用的API,可以方便地在代码中使用锁。它支持不同的锁提供者,包括数据库(如MySQL、PostgreSQL)、ZooKeeper等。此外,Shedlock还提供了一些高级特性,如自动解锁、锁超时、定时任务等,以满足不同的场景需求。

总的来说,Shedlock是一个可靠、高效的分布式锁库,可以帮助开发者在分布式环境中处理并发问题,保证数据的一致性和正确性。它的设计简单,易于集成和使用,是Java开发者的理想选择之一。

shedlock + db

  1. 创建表
sql 复制代码
CREATE TABLE `shedlock` (
  `name` varchar(64) COLLATE utf8mb4_bin NOT NULL,
  `lock_until` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  `locked_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  `locked_by` varchar(255) COLLATE utf8mb4_bin NOT NULL,
  PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
  1. 引入依赖
xml 复制代码
        <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>
  1. 配置LockProvider
java 复制代码
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;

import javax.sql.DataSource;

import static net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider.Configuration.builder;

@Configuration
@EnableScheduling  //开启定时任务
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S") //默认锁的最大占有30秒
public class SchedulerConfiguration {

    @Autowired
    DataSource dataSource;

    @Bean
    public LockProvider lockProvider() {

//        //可以自定义数据源,可以作为一种考虑,一般不使用这个
//        org.apache.tomcat.jdbc.pool.DataSource dataSource1 = new org.apache.tomcat.jdbc.pool.DataSource();
//        dataSource1.setUrl("jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf8&useSSL=false");
//        dataSource1.setUsername("root");
//        dataSource1.setPassword("123456");

        LockProvider lockProvider = new JdbcTemplateLockProvider(builder()
                //指定表名
                .withTableName("shedlock")
                //指定数据源,一般使用dataSource而非手动定义的数据源
                .withJdbcTemplate(new JdbcTemplate(dataSource))
                //指定表字段名称,字段数量固定,只能改名称,且只有较高版本的shedlock-provider-jdbc-template依赖才提供该配置项
//                .withColumnNames(new JdbcTemplateLockProvider.ColumnNames("name","lock_until","locked_at","locked_by"))
                //使用数据库时间,只有较高版本的shedlock-provider-jdbc-template依赖才提供该配置项
//                .usingDbTime()
                //作用未知,只有较高版本的shedlock-provider-jdbc-template依赖才提供该配置项
//                .withLockedByValue("myvalue")
                //作用未知,只有较高版本的shedlock-provider-jdbc-template依赖才提供该配置项
//                .withIsolationLevel(1)
                .build());
        return lockProvider;
    }
}
  1. 使用
java 复制代码
@Slf4j
@Component
@EnableScheduling //开启定时任务
public class MyJob {

    @Scheduled(cron = "0,15,30,45 * * * * ?")
    @SchedulerLock(name = "task1", lockAtLeastForString = "PT5S", lockAtMostForString = "PT10S")
    public void task1() {
        log.info("-----task1-------");
    }
}    

SchedulerLock注解说明

java 复制代码
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SchedulerLock {
    /**
    * 任务的名称,同名任务互斥,只会执行一个
    */
    String name() default "";

    /**
    *  锁的最多占用多久,单位毫秒
    */
    long lockAtMostFor() default -1L;

    /**
    * 锁的最多占用多久,字符格式,PT2S 表示2秒,PT2M表示2分钟
    */
    String lockAtMostForString() default "";

    /**
    * 锁的最少占用时间,单位毫秒
    */
    long lockAtLeastFor() default -1L;

    /**
    * 锁的最少占用时间,字符格式,PT2S 表示2秒,PT2M表示2分钟
    */
    String lockAtLeastForString() default "";
}

shedlock + redis

引入依赖,注意建议版本相同,避免可能带来的版本兼容问题。

  1. 引入依赖
xml 复制代码
 <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-redis-spring</artifactId>
            <version>2.2.0</version>
        </dependency>
  1. 配置LockProvider
java 复制代码
    @Bean
    public LockProvider lockProvider(RedisTemplate redisTemplate) {
        return new RedisLockProvider(redisTemplate.getConnectionFactory());
    }

遇到的问题

1.配置shedlock不生效

  • 在非springboot中,请确保配置的bean是否被扫描到,确保LockProvider是否被加载到容器中。
  • 是否定时任务开启了线程池等
相关推荐
8Qi812 小时前
DDD架构演进
java·分布式·mvc·ddd
方方洛13 小时前
ray教程-23-附录C-术语表
人工智能·分布式·机器学习
海棠Flower未眠20 小时前
下单请求处理流程:商品维度分布式锁与 Redis 预扣库存
redis·分布式·junit
罗四强1 天前
第10篇:打包上架:签名、打包 HAP、发布到华为应用市场
分布式·操作系统·harmonyos·arkts·鸿蒙系统
秋饼1 天前
Redis 全面解析:从数据结构到主从/哨兵/集群、分布式锁与面试高频考点,代码到原理一次讲透
数据库·redis·分布式·面试·后端开发
二月夜1 天前
Redis 高频面试题全解:缓存三大问题、大 key 治理与分布式锁之争(2026 招聘 JD 真题复盘)
redis·分布式·后端·缓存
樱花落木兰1 天前
Redis 消息队列的可靠性演进:从 List、Pub/Sub 到 Stream 消费者组,以及 8.x 版本改变了什么
java·开发语言·经验分享·redis·分布式·python·软件工程
真上帝的左手1 天前
12. 消息队列-Kafka进阶
分布式·中间件·kafka
Thanks_ks2 天前
Cookie 和 Session 到底是什么关系——登录态是怎么维持的
分布式·http·jwt·cookie·session·后端开发·登录态
Sayai2 天前
Kafka 跨数据中心日志同步实战:MirrorMaker 2 选型对比 + Flink 中继任务落地(双中心互备与消费者组隔离避坑)
大数据·运维·分布式·flink·kafka·消息队列·高可用