shedlock 重启系统报错问题_使用shedlock实现分布式定时任务锁

定时器Scheduler在平时使用比较频繁(Spring定时任务:https://www.jianshu.com/p/570d6820b054 )在springboot中,配置好@Scheduled和@EnableScheduling之后,定时器就能正常执行,实现定时任务的功能。

但是在这样的情况下:如果开发的服务需要水平部署实现负载均衡,那么定时任务就会同时在多个服务实例上运行,那么一方面,可能由于定时任务的逻辑处理需要访问公共资源从而造成并发问题;另一方面,就算没有并发问题,那么一个同样的任务多个服务实例同时执行,也会造成资源的浪费。因此需要一种机制来保证多个服务实例之间的定时任务正常、合理地执行。

ShedLock的出现就是为了解决上述问题,它可以保证多个一个定时任务在多个服务实例之间最多只执行一次,是一个在分布式环境中保证定时任务合理执行的框架,我们可以叫它分布式定时任务锁。

ShedLock的实现原理是采用公共存储实现的锁机制,使得同一时间点只有第一个执行定时任务的服务实例能执行成功,并在公共存储中存储"我正在执行任务,从什么时候(预计)执行到什么时候",其他服务实例执行时如果发现任务正在执行,则直接跳过本次执行,从而保证同一时间一个任务只被执行一次。

上面提到的公共存储目前支持的有:

Monogo

DynamoDB

JdbcTemplate

ZooKeeper (using Curator)

Redis (using Spring RedisConnectionFactory)

Redis (using Jedis)

Hazelcast

值得注意的是,ShedLock不是一个分布式的定时任务框架,只是一个锁,用于保证分布式环境中的定时任务合理执行。

这里使用JdbcTemplate,新建springboot项目,pom文件如下:

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

poc

com.hkj

0.0.1-SNAPSHOT

4.0.0

shedlock

1.8

org.springframework.boot

spring-boot-starter

mysql

mysql-connector-java

org.springframework.boot

spring-boot-starter-jdbc

2.2.2.RELEASE

net.javacrumbs.shedlock

shedlock-spring

4.11.1

net.javacrumbs.shedlock

shedlock-provider-jdbc-template

4.11.1

org.springframework.boot

spring-boot-starter-test

test

org.junit.vintage

junit-vintage-engine

org.springframework.boot

spring-boot-maven-plugin

shedlock配置类如下:

package com.hkj.config;

import net.javacrumbs.shedlock.core.LockProvider;

import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.jdbc.core.JdbcTemplate;

import javax.annotation.Resource;

import javax.sql.DataSource;

import java.util.TimeZone;

/**

* @descrition shedlock配置类

* @since 2020-06-01 15:19

*/

@Configuration

public class ShedlockConfig {

@Resource

private DataSource dataSource;

/**

* @description 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 )

* );

* @date 2020/6/1 15:19

*/

@Bean

public LockProvider lockProvider() {

return new JdbcTemplateLockProvider(

JdbcTemplateLockProvider.Configuration.builder()

.withJdbcTemplate(new JdbcTemplate(dataSource))

.withTimeZone(TimeZone.getTimeZone("GMT+8"))

.build()

);

}

}

在MySQL中添加一张名为shedlock的表

shedlock表

通过修改数据库表中的数据实现锁。

表字段说明:

主键name:每个定时任务的一个名字

locked_at:锁的开始时间

lock_until:锁的结束时间

再定时开始时,会更新这两个时间,在时间之内的定时是不会被执行的。

yml配置

server:

port: 8080

spring:

datasource:

name: test

url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai

username: root

password: lovehkj

driver-class-name: com.mysql.cj.jdbc.Driver

启动类上开启shedlock和定时任务

package com.hkj;

import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication

@EnableScheduling

// 指定锁默认持有时间

@EnableSchedulerLock(defaultLockAtMostFor = "10m")

public class ShedLockStarter {

public static void main(String[] args) {

SpringApplication.run(ShedLockStarter.class, args);

}

}

新建定时任务

package com.hkj.job;

import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;

import java.util.Date;

/**

* @descrition

* @since 2020-06-01 15:09

*/

@Component

public class TestJob {

/**

* @description 每隔1min打印一次

* @date 2020/6/1 15:10

*/

@Scheduled(cron = "0 0/1 * * * ?")

// lockAtMostFor为锁默认持有时间,会覆盖启动类中的默认持有时间

@SchedulerLock(name = "print", lockAtMostFor = "3m")

public void print() {

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式

System.err.println(df.format(new Date()));// new Date()为获取当前系统时间

}

}

@Scheduled注解有五个参数

name:定时任务的名字,就是数据库中的内个主键

lockAtMostFor:锁的最大时间单位为毫秒

lockAtMostForString:最大时间的字符串形式,例如:PT30S 代表30秒

lockAtLeastFor:锁的最小时间单位为毫秒

lockAtLeastForString:最小时间的字符串形式

分别启动两个实例8080和8081

观察console输出结果:

8080实例

8081实例

此时已经不会发生同一时间段多个实例执行同一调度任务的情况了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值