首发于个人博客:ShedLock日常使用

场景模拟

定时器Scheduler在平时使用比较频繁,比如定时数据整理,定时向客户发送问候信息等...,定时任务的配置比较简单,比如在springboot中,配置好@Scheduled@EnableScheduling之后,定时器就能正常执行,实现定时任务的功能。

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

ShedLock介绍

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

Github项目地址:https://github.com/lukas-krecan/ShedLock.

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

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

  • Monogo
  • DynamoDB
  • JdbcTemplate
  • ZooKeeper (using Curator)
  • Redis (using Spring RedisConnectionFactory)
  • Redis (using Jedis)
  • Hazelcast

值得注意的是,ShedLock不是一个分布式的定时任务框架,只是一个锁,用于保证分布式环境中的定时任务合理执行。贴一段原话:Please note that ShedLock is not and will never be full-fledged scheduler, it's just a lock. If you need a distributed scheduler, please use another project. ShedLock is designed to be used in situations where you have scheduled tasks that are not ready to be executed in parallel, but can be safely executed repeatedly. For example if the task is fetching records from a database, processing them and marking them as processed at the end without using any transaction. In such case ShedLock may be right for you.

依赖说明:

  • Java 8
  • slf4j-api
  • Spring Framework (optional)

ShedLock使用

以SpringBoot为例。

引入依赖:

<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>2.3.0</version>
</dependency>

定义一个定时任务:

@Component
public class DataHouseKeepingScheduler {
@Scheduled(cron = "*/5 * * * * ?")
private void dataHouseKeeping(){
System.out.println(String.format("[%s] DataHouseKeeping job run...", new Date()));
}
}

添加ShedLock

在主类或者@EnableScheduling的地方添加@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")

@SpringBootApplication
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

然后在@Scheduled(...)的地方添加@SchedulerLock(name = "scheduledTaskName")

@Component
public class DataHouseKeepingScheduler {
@Scheduled(cron = "*/5 * * * * ?")
@SchedulerLock(name = "dataHouseKeepingLock", lockAtLeastForString = "20000", lockAtMostForString = "30000")
private void dataHouseKeeping(){
System.out.println(String.format("[%s] DataHouseKeeping job run...", new Date()));
}
}

@SchedulerLock的作用是保证当前定时任务的方法执行时获得锁,忽略其他相同任务的执行。但是,name必须要指定,ShedLock就是根据这个name进行相同任务判定的(后面会细说)。注解中还有两个参数:

  • lockAtLeastForString: 锁持有的最小时间(ms)。比如示例代码中设定的5000ms表示当前方法执行时获取到的锁的最小时间是20s,20s之内,就算当前定时任务已经执行完成,其他任务也无法获得锁,必须等5s之后才能获取。
  • lockAtMostForString: 锁持有的最大时间(ms)。当前任务获取的锁的最大持有时间为30s,30s之后就算没有执行完,其他定时任务也可以获取锁去执行任务。

配置LockProvider,此处以jdbc为例。先引入依赖:

<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>2.3.0</version>
</dependency>

配置@Bean:

@Component
public class ShedLockConfig {
@Resource
private DataSource dataSource; @Bean
private LockProvider lockProvider(){
return new JdbcTemplateLockProvider(dataSource);
}
}

此处的DataSource即为当前应用的数据源。

注意,如果使用SqlServer需要设置schema,可以在JdbcTemplateLockProvider的构造方法中设置:

new JdbcTemplateLockProvider(dataSource, "schema.shedlock")

创建存储锁信息的表。 MySQL ```sql
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)
);

启动应用,定时任务开始执行:

[Mon Feb 25 22:38:15 CST 2019] DataHouseKeeping job run...
[Mon Feb 25 22:38:35 CST 2019] DataHouseKeeping job run...
[Mon Feb 25 22:38:55 CST 2019] DataHouseKeeping job run...

可以看到,虽然定时任务设定的是每5秒执行一次,但是控制台输出的却是每20秒一次,这时就是lockAtLeastForString="20000"生效了。

这时候来看看表里的数据:

总结

再次强调,ShedLock不是一个定时任务框架,而是一个保证定时任务在分布式环境中的合理执行的辅助框架,保证定时任务在分布式环境中同一时间最多只执行一次。同时一个任务在执行时,另一个任务无法获取锁时会跳过当前任务的执行。

最后关于ShedLock的版本说明(直接贴原话了):

Version 1.x.x is compiled and tested with Spring 5 and Spring Data 2. It should be safe to use ShedLock 1.x.x with Spring 4 if you are not using Spring Redis lock provider which introduced incompatibility. In other words

  • If you have dependency on spring-data-redis 2 - use ShedLock 1.x.x
  • If you have dependency on spring-data-redis 1 - use ShedLock 0.x.x
  • In all other cases, you can use both versions, prefereably 1.x.x

Demo源码地址

Demo源码地址:https://gitee.com/nickhan/ShedLockDemo

ShedLock日常使用的更多相关文章

  1. ORACLE分区表梳理系列(二)- 分区表日常维护及注意事项(红字需要留意)

    版权声明:本文发布于http://www.cnblogs.com/yumiko/,版权由Yumiko_sunny所有,欢迎转载.转载时,请在文章明显位置注明原文链接.若在未经作者同意的情况下,将本文内 ...

  2. Java程序员的日常—— Arrays工具类的使用

    这个类在日常的开发中,还是非常常用的.今天就总结一下Arrays工具类的常用方法.最常用的就是asList,sort,toStream,equals,copyOf了.另外可以深入学习下Arrays的排 ...

  3. 数据库日常维护-CheckList_03有关数据库数据文件大小检查

    日常数据维护中容量规划是每个DBA的基础工作之一,也是非常重要的工作.在生产环境中一些比较重要的业务数据库会用前期容量规划不足,或出现意外的爆发式的数据增长,直至盛满整个磁盘空间,系统会无法使用,最终 ...

  4. 数据库日常维护-CheckList_02有关数据库备份检查

    数据库备份是DB日常运维中最基本的也是最重要的工作,很多情况下都是做成作业形式实现自动化周期性的做全备.差异以及日志备份.那么,如果作业出现问题没有完成工作,我们可以设置自动报警如email被动提醒我 ...

  5. 数据库日常维护-CheckList_01历史Agent Job执行情况检查

    检查Agent Job中日常维护作业或业务作业是否成功,如每天的备份.碎片整理.索引维护.历史备份文件清除等,可利用SSMS工具,通过CDC下面设置好的DB Server List,运行下面脚本一次, ...

  6. [No000094]SVN学习笔记4-版本库概念与部分日常操作

    基本概念 版本库 Subversion 使用集中的数据库,它包含了所有的版本控制文件及其完整历史.这个数据库就是版本库.版本库通常位于运行 Subversion 服务器的文件服务器上,向 Subver ...

  7. 【CentOS】Linux日常管理

    /////////////////////////目录///////////////////////////////////////// 一.日常监控指标相关 1.监控系统状态命令 2.查看系统进程 ...

  8. 日常办公工具利器 notepad++

    日常办公工具利器 notepad++ 文本内容比较 Notepad++ https://notepad-plus-plus.org/ http://jingyan.baidu.com/article/ ...

  9. Maven日常 —— 你应该知道的一二三

    以前在日常工作中,使用Maven只是机械的执行Maven clean.Maven install,对其中的原理与过程并无了解,近期阅读了<Maven实战>,对Maven有了更深入的理解. ...

随机推荐

  1. DNS域名解析系统_1

    DNS服务概述: DNS的模式为C/S模式 DNS(Domain Name System)域名系统,在TCP/IP网络中有非常重要的地位,能够提供域名与ip地址的解析服务. DNS是一个分布式数据库, ...

  2. hi cnblogs

    hi cnblogs 前言: 作为一个工作好些年的it男,还没正经写过技术博客,真是太羞愧了... 正文: 先扯点儿虚的,无论从事什么行业,最重要的都是自我认知和自我定位.至今,我也有工作了小五年了, ...

  3. 最新 优刻得java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.优刻得等10家互联网公司的校招Offer,因为某些自身原因最终选择了优刻得.6.7月主要是做系统复习.项目复盘.LeetCo ...

  4. 冲刺Noip2017模拟赛4 解题报告——五十岚芒果酱

    题1 韬韬抢苹果(apple) [问题描述] 又到了收获的季节,树上结了许多韬韬,错了,是许多苹果,有很多个小韬韬都来摘苹 果.每个韬韬都想要最大的苹果,所以发生了争执,为了解决他们的矛盾,出题人定了 ...

  5. poj3107(树的重心,树形dp)

    题目链接:https://vjudge.net/problem/POJ-3107 题意:求树的可能的重心,升序输出. 思路:因为学树形dp之前学过点分治了,而点分治的前提是求树的重心,所以这题就简单水 ...

  6. [CF1146D]Frog Jumping_exgcd_堆优化dij

    Frog Jumping 题目链接:http://codeforces.com/contest/1146/problem/D 数据范围:略. 题解: 首先发现,如果$x\ge a +b$,那么所有的$ ...

  7. python接口测试

    引入unittest框架 get请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #coding: utf-8 import un ...

  8. 1.3.3 并发容器类MAP/LIST/SET/QUEUE

    HashMap 下标计算方法:hashCode & (length-1) ,&按位与操作,二进制相同位都是1,该位才为1 JDK1.7与JDK1.8中HashMap区别: JDK1.8 ...

  9. TypeScript的类型

    ⒈TypeScript的类型 JavaScript语言的数据类型包括以下7种: 1.boolean(布尔),true || false 2.null,表明null值得特殊关键字,JavaScript是 ...

  10. Jmeter博文索引~基础知识和实践操作汇总

    所有Jmeter笔记的目录/索引 一,基础操作和常用操作 Jmeter入门(一)理论基础 Jmeter安装及配置(含JDK安装) Jmeter之设置线程组运行次数/时间 Jmeter之参数化(4种设置 ...