集群及分布式定时任务中间件MEE_TIMED
集群及分布式定时任务中间件MEE_TIMED
MEE_TIMED一套开源的定时任务中间件,MEE_TIMED 简化了 scheduled及shedlock的配置,同时也升级了这两种中间件的能力 ,使定时任务开发更具灵活性的同时
具备集群及分布式节点的管理,同时也增加了传参,使之更加强大
开发初衷
目前 java 语言下可用的定时任务基础组件无非这俩: spring scheduled 以及 quartz,其中 scheduled 属于轻量级的设计 默认集成在 spring-context 包中,所以springboot使用 scheduled 简单快捷,
既然简单也必有简单的局限(后面会聊),quartz 则属于重量级的设计,内部提供了 RMI 及 JMX 支持 以及使用基于DB的行锁使之支持集群,这都很好,不过内部代码设计及扩展似乎过于臃肿,不使用表又会退化为 scheduled ~
有时,项目不大不小,但是有集群需求并且需要保证任务不重复执行,这时就需要 scheduled+shedlock 这样的搭配,可这样无法动态传参,同时增加了业务代码的复杂度,这是问题;
当然也可以使用 quartz+数据库表 的方式 则管理集群及节点任务会变得比较复杂, 而且任务的启停及关闭操作在分布式环境下使用 quartz 提供的api操作尤其的麻烦,这也是问题...
spring scheduled所面临的问题:CRON表达式过于简单,不支持复杂的表达式,比如每月最后一天,虽然提供zone支持但在特殊的国度,如在美国,无法计算夏令时及冬令时的偏差- 当
@Schedules与@SchedulerLock配合时 多执行时间 会存在被锁定的问题 scheduled如果不指定线程池时 默认是单线程执行,不管应用下有多少定时任务都会是单线程,这是瓶颈...scheduled不支持传参,函数使用时必须是void的函数返回且不可有形参- 部分api可能存在
spring版本迭代时不兼容问题,这是二开可能的问题
shedlock的不足之处:- 无法做集群及分布式节点管理,除非key定义的十分小心
- 不太好通过锁的控制做任务及节点的启停控制(可以通过特殊方法 比较另类)
- 任务执行时的关键信息默认不记录(IP、时间、CRON、应用信息等等)
- 加锁过程可能存在不必要的更新操作(这是代码问题)
基于现有情况我改造了 scheduled,用较少的更改 做出了处于 scheduled 及 quartz 中间的定时任务组件,这就是 MEE_TIMED .
MEE_TIMED 所做的改进
- 新增
app表(SYS_SHEDLOCK_APP),提供集群及多节点控制支持 - 扩展
job(SYS_SHEDLOCK_JOB)表data字段,提供传参及参数修改支持 @Schedule与@SchedulerLock二合一并简化注解配置spring scheduled的CronExpression替换为quartz的CronExpression,支持更灵活更复杂的CRON表达式- 修改掉
scheduled内部默认单线程的问题,提供线程池支持 - 固定于spring强绑定的api,尽量与
springboot兼容性做到最佳 - 任务信息落表 等等
基本使用
详细配置代码及后台集成在mee-admin有实例 (,)
1.下载 表结构 及 mee_timed-X.X.X.jar 依赖 依赖 并存放于项目或nexus私服中
2.POM中定义dependency依赖:
<dependency>
<groupId>com.mee.timed</groupId>
<artifactId>mee_timed</artifactId>
<version>1.0.1</version>
<scope>system</scope>
<systemPath>${pom.basedir}/src/main/resources/lib/mee_timed-1.0.1.jar</systemPath>
</dependency>
3.导入表结构(SQL)
根据所使用的
db,按需导入对应厂商所支持的表结构,目前仅提供mysql、oracle、postgresql支持:table_mysql.sql
table_oracle.sql
table_postgresql.sql
4.定义配置及bean
目前配置仅有三项:
spring.mee.timed.shed=${spring.application.name}
spring.mee.timed.table-name=SYS_SHEDLOCK_JOB
spring.mee.timed.table-app-name=SYS_SHEDLOCK_APP
其中配置项
spring.mee.timed.table-app-name是管理集群及节点用的,如不需要可不配置
应用启动时会自动写入必要的初始化参数,也可提前将初始数据提前导入配置bean: 这一步是非必须的,只是内部线程池的配置较为保守,如需自定义可以以下配置指定线程数及线程名前缀:
/**
* 设置执行线程数
* @return
*/
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(PROCESSOR*2);
scheduler.setThreadNamePrefix("SHEDLOCK-");
scheduler.initialize();
return scheduler;
}
5.定义定时任务
样例一:
import com.mee.timed.Job;
import com.mee.timed.JobExecutionContext;
import com.mee.timed.annotation.MeeTimed;
import com.mee.timed.annotation.MeeTimeds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; @Component
public class Job01TestService implements Job {
private static final Logger LOGGER = LoggerFactory.getLogger(Job01TestService.class); @MeeTimed(fixedRate = 10000,lockAtLeastFor = "PT5S",lockAtMostFor ="PT5S" )
public void exec01() throws InterruptedException {
LOGGER.info("=====> [exec01] Already Executed! <=====");
TimeUnit.SECONDS.sleep(6);
} @MeeTimeds({
@MeeTimed(cron = "10,20,30,40,50 * * * * ?",lockAtMostFor ="PT5S",lockName = "execute1"),
@MeeTimed(cron = "0 0/2 * * * ?",lockAtMostFor ="PT1M",lockName = "execute2"),
@MeeTimed(cron = "0 0/4 * ? * MON-FRI",lockAtMostFor ="PT1M",lockName = "execute3"),
// 纽约时间每年的7月9号22点2分执行
@MeeTimed(cron = "0 2 22 9 7 ?",lockAtMostFor ="PT1M",lockName = "execute4",zone = "America/New_York"),
// 每月最后一天的十点半(eg:2024-07-31 10:30:00)
@MeeTimed(cron = "0 30 10 L * ?",lockAtMostFor ="PT1M",lockName = "execute5")
})
@Override
public void execute(JobExecutionContext context) {
LOGGER.info("=====> proxy job exec! data:"+context.getJobInfo().getName()+" <=====");
try {
TimeUnit.SECONDS.sleep(8);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} }
样例二:
package com.mee.timed.test.job; import com.mee.timed.annotation.MeeTimed;
import com.mee.timed.annotation.MeeTimeds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; @Component
public class ScheduledTasks {
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTasks.class); @MeeTimeds({
@MeeTimed(fixedRate = 10000,lockAtLeastFor = "PT5S",lockAtMostFor ="PT5S",lockName = "T1"),
@MeeTimed(fixedDelay = 8000,lockAtLeastFor = "PT5S",lockAtMostFor ="PT5S",lockName = "T2"),
})
public void exec01() {
LOGGER.info("=====> [exec01] Already Executed! <=====");
} @MeeTimed(cron = "0/20 * * * * ?",lockAtLeastFor = "PT5S",lockAtMostFor ="PT10S" )
public void exec02(JobExecutionContext context) {
LOGGER.info("=====> proxy job exec! data:"+context.getJobDataJson()+" <=====");
} }
以上两种方式均可,如果需要传递参数 其函数的形参数 必须是
JobExecutionContext或其实现类如果是同一函数多时间配置(使用
@MeeTimeds配置),其每一项lockName不可为空!
集成后台管理
具体效果及代码集成 具体见: mee-admin
后台配置及管理


实际执行效果

2024-07-18 09:59:20.006 -> [MEE_TIMED-7] -> INFO com.mee.cron.JobTimedService:25 - =====> proxy job exec! data:{"key":"执行数据"} <=====
2024-07-18 09:59:40.020 -> [MEE_TIMED-7] -> INFO com.mee.cron.JobTimedService:25 - =====> proxy job exec! data:{"key":"执行数据"} <=====
2024-07-18 09:59:59.993 -> [MEE_TIMED-1] -> INFO com.mee.cron.DefaultTimerService:27 - ===>testTask2執行時間: 2024-07-18 09:59:59
2024-07-18 10:00:00.003 -> [MEE_TIMED-5] -> INFO com.mee.cron.DefaultTimerService:21 - ===>testTask1執行時間: 2024-07-18 10:00:00
2024-07-18 10:00:00.009 -> [MEE_TIMED-4] -> INFO com.mee.cron.JobTimedService:25 - =====> proxy job exec! data:{"key":"执行数据"} <=====
2024-07-18 10:00:20.014 -> [MEE_TIMED-4] -> INFO com.mee.cron.JobTimedService:25 - =====> proxy job exec! data:{"key":"执行数据"} <=====
2024-07-18 10:00:40.015 -> [MEE_TIMED-4] -> INFO com.mee.cron.JobTimedService:25 - =====> proxy job exec! data:{"key":"执行数据"} <=====
2024-07-18 10:01:00.019 -> [MEE_TIMED-4] -> INFO com.mee.cron.JobTimedService:25 - =====> proxy job exec! data:{"key":"执行数据"} <=====
后续计划
首先是传参考虑做反序列化处理,在必要场景下这是需要的
fix bug,当然这需要码友多多支持啦
动态修改执行时间,尤其是
cron,这功能是与quartz的差距的缩小是决定性的执行日志支持,并提供扩展支持
其他待定
最后
再次感谢 spring scheduled 及 shedlock 的开源,MEE_TIMED 在 github 有开源,详见: https://github.com/funnyzpc/mee_timed_parent
集群及分布式定时任务中间件MEE_TIMED的更多相关文章
- Quartz集成springMVC 的方案二(持久化任务、集群和分布式)
Quartz是一个开放源码项目,专注于任务调度器,提供了极为广泛的特性如持久化任务,集群和分布式任务等. Quartz核心是调度器,还采用多线程管理. 1.持久化任务:当应用程序停止运行时,所有调度信 ...
- 3-3 Hadoop集群完全分布式配置部署
Hadoop集群完全分布式配置部署 下面的部署步骤,除非说明是在哪个服务器上操作,否则默认为在所有服务器上都要操作.为了方便,使用root用户. 1.准备工作 1.1 centOS6服务器3台 手动指 ...
- web中的集群与分布式
面试中经常会提到 集群 和 分布式.下面就来分别说说这两个在web开发中经常用到的开发方式. 集群: 集群是一组协同工作的服务实体,用以提供比单一服务实体更具扩展性与可用性的服务平台.在客户端看来,一 ...
- Redis集群与分布式介绍以及搭建Redis-Cluster
1 Redis集群 1.1 什么是集群 集群就是很多服务器组成的一个网络.指的是将多台服务器集中在一起,实现同一业务. 1.2 为什么要集群 一台服务器不能满足开发需要的时候,需要多台服务器来支持.这 ...
- Hadoop1.X集群完全分布式模式环境部署
Hadoop1.X集群完全分布式模式环境部署 1 Hadoop简介 Hadoop是Apache软件基金会旗下的一个开源分布式计算平台.以Hadoop分布式文件系统(HDFS,Hadoop Distri ...
- 集群、分布式、SOA、微服务、webService等思想的整理
引子:前几天甲方问我,他用wpf弄个界面,能不能通过其他语言给他传输数据,我由此想到了webservice(此时此刻,我也没有用过webServices),作日翻阅了一些资料,对这块技术有了个大概的了 ...
- 无人机集群的分布式协作 VI-SLAM
以下内容来自从零开始机器人SLAM知识星球 每日更新内容 点击领取学习资料 → 机器人SLAM学习资料大礼包 论文# D2SLAM: Decentralized and Distributed Col ...
- web集群和分布式服务以及消息补偿机制几种方案
一.为什么要集群? 1.JavaEE项目,如果部署在一台Tomcat上,所有的请求,都由这一台服务器处理,存在很大风险: A:并发处理能力有限(一般单台服务器处理的并发量为250左右,超过250,可能 ...
- JavaEE学习文章汇总-并发,集群,分布式
以下文章来自博客 http://blog.csdn.net/FX_SKY/article/category/6203839 其中包括 集群Zookeeper 环境搭建 http://blog.csdn ...
- 大数据系列(3)——Hadoop集群完全分布式坏境搭建
前言 上一篇我们讲解了Hadoop单节点的安装,并且已经通过VMware安装了一台CentOS 6.8的Linux系统,咱们本篇的目标就是要配置一个真正的完全分布式的Hadoop集群,闲言少叙,进入本 ...
随机推荐
- liunx下redis的哨兵环境搭建
哨兵简介 一定要有一个概念:哨兵实例也是特殊的Redis实例,也就是哨兵实例是独立的进程,多个哨兵实例可以搭建主从(Master-Slave),它们承担的职责和普通的Redis实例不一样.下面是官方文 ...
- 三:nacos的配置中心
配置中心的使用: 编辑当前项目的pom.xml,加入必要的依赖配置 <!-- spring-cloud-alibaba-dependencies 依赖同注册中心 --> <depen ...
- 【C#】安装服务相关
判断C#写的服务版本:一般就是v2.0.50727和v4.0.30319这两个 Assembly currentAssembly = Assembly.LoadFile(filePath); var ...
- TS码流解析(一)TS Header
有一些音视频初学者想要了解TS码流结构,但网上资料不全或者讲得不够清楚,使得学习过程变得异常艰难.这一篇内容将对TS码流结构做详尽解析,争取做到通俗易懂,成为最好的TS码流解析文章. 本篇TS码流解析 ...
- 【jetson nano】烧录系统
烧录固件 烧录固件是为了让板子用tf卡作为系统启动(非板载启动),一般来说只需要刷写一遍. 安装vm,找到虚拟机镜像,解压part01就能获取镜像. 打开vm,打开此虚拟机镜像,账号clb,密码为12 ...
- lodash已死?radash库方法介绍及源码解析 —— 判断方法篇
前言 大家好,我是阿瓜.一个励志分享更多技术的前端瓜 ~ 我们已经分享了 radash 库中数组.对象等相关的方法,大家感兴趣的可以前往主页查看阅读: 或许你最近在某个地方听过或者看过 radash ...
- SpringBoot系列(三)元注解
元注解,注解的注解,SpringBoot有四个元注解,分别是@Target.@Retention.@Documented.@Inherited.下面就是对元注解的详细讲解和源码展示. @Taget 该 ...
- WIN11 删除其它用户/账户
WIN11 删除其它用户/账户 参考:https://support.microsoft.com/zh-cn/windows/%E5%9C%A8%E7%94%B5%E8%84%91%E4%B8%8A% ...
- Vue3:介绍
Vue 3 相较于 Vue 2 在多个方面进行了改进和优化,主要优势包括但不限于以下几个方面: 响应式系统优化: Vue 3 引入了基于 Proxy 的响应式系统,取代了 Vue 2 中基于 Obje ...
- MySQL插入中文数据时发生错误或者乱码的一些坑
最近新入职的工作,火急火燎就下了个mysql,没想到安装时配置没弄好.今天在测试数据时,插入中文数据到mysql都是问号,先后查了半天修改表结构,数据库编码,my.ini文件都没有用. 首先第一步,打 ...