【SpringBoot】集成ShedLock实现分布式锁(JdbcTemplate)

背景

在分布式多机集群环境下,一个服务可能有多个实例,其中的定时任务@Scheduled也会多次触发。在一些业务场景下,我们只希望他执行一次,此时可通过@SchedulerLock来对该定时任务进行加锁从而控制定时任务执行次数(也可通过Redis实现分布式锁)。
同时本次案列也配合线程池实现定时任务的多线程处理

依赖

     <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>

启用注解

  • 配置类启用
@Component
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class ShedLockConfig {
    @Bean
    private LockProvider lockProvider(DataSource dataSource) {
        //return new JdbcTemplateLockProvider(dataSource);
        //自定义表名t_shedlock 默认表名shedlock
        return new JdbcTemplateLockProvider(JdbcTemplateLockProvider.Configuration
        	.builder()
        	.withJdbcTemplate(new JdbcTemplate(dataSource))
        	.withTableName("t_shedlock")
        	.build());
    }
}
  • 定时任务线程池配置
@EnableScheduling
@Configuration
public class ScheduleConfig implements SchedulingConfigurer{

 	@Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
         ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskExecutor();
         scheduler.setPoolSize(50);
         scheduler.setThreadNamePrefix("taskScheduler-");
         scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
         scheduler.setWaitForTaskToCompleteOnShutdown(true);
         scheduler.setAwaitTerminationSeconds(60);
         scheduler.initialize();
         taskRegistrar.setScheduler(scheduler);
     }
    
}
注解含义
@EnableScheduling开启定时任务
@EnableSchedulerLock开启定时任务锁,defaultLockAtMostFor属性表示默认最大锁持有时间,PT30S表示30秒。
@EnableAsync开启异步

创建数据库表(mysql)

//自定数据表名(mysql 8.0)
CREATE TABLE `t_shedlock`  (
  `name` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,
  `lock_until` timestamp(3) NULL DEFAULT NULL,
  `locked_at` timestamp(3) NULL DEFAULT NULL,
  `locked_by` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  PRIMARY KEY (`name`) 
) ENGINE = InnoDB DEFAULT SET = utf8mb4 COLLATE = utf8mb4_general_ci;

创建定时任务

这里为什么创建两个一模一样的定时任务, @SchedulerLock能够在不同实例(节点)进行加锁操作,保证任务执行一次,但@Scheduled在一个节点上是单线程的,也就是说@Scheduled定时任务在单节点执行时间点一致时,不可避免出现线程阻塞的问题,这也就是本次案例为什么一定要配合异步线程使用,同时解决不同节点和同一节点定时任务互相影响的情况,可根据实际情况自行选择策略。


@Component
public class CustomizeSchedule {

    @Scheduled(cron = "0/5 * * * * ? ")	// 5秒执行一次
    @SchedulerLock(name = "test2",lockAtLeastFor = 30 * 1000,lockAtMostFor = 60 * 60 * 1000)
    public void test1() {
        System.out.println(Thread.currentThread());
    }

    @Scheduled(cron = "0/5 * * * * ? ")	// 5秒执行一次
    @SchedulerLock(name = "test1",lockAtLeastFor = 30 * 1000,lockAtMostFor = 60 * 60 * 1000)
    public void test2() {
        System.out.println(Thread.currentThread());
    }
    
}

注解含义
@Async自定义异步执行
@Scheduled定时任务执行
@SchedulerLock定时任务锁

@SchedulerLock

属性含义
name分布式锁名称,锁名称必须是唯一的
lockAtLeastFor指定在执行节点应保留锁定的最短时间。主要目的是在任务非常短的且节点之间存在时钟差异的情况下防止多个节点执行。这个属性是锁的持有时间,设置了多少就一定会持有多长时间,在此期间,下一次任务执行时,其他节点包括它本身是不会执行任务的;单位:毫秒
lockAtMostFor指定在执行节点死亡时应将锁保留多长时间。这只是一个备用选项,在正常情况下,任务完成后立即释放锁定。 使用时必须将其设置为比正常执行时间长得多的值。如果任务花费的时间超过可能无法预测(更多的进程将有效地持有该锁);单位:毫秒
  • 注意:

合理设置@SchedulerLock属性,防止定时任务漏掉或重复执行

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值