Cron-Job是一个简单易用、低延迟的分布式任务调度平台,支持用户权限管理、多语言客户端和多租户接入。该平台支持任何Cron表达式的任务调度,并提供常见的分片与随机策略;支持失败丢弃与失败重试等策略,并能够动态调整任务参数。
一、为什么需要分布式任务调度平台
假设有一个场景,需要在每天凌晨1点执行统计任务。传统方式可能使用分布式锁(如Redis、ZK、MySQL等)来确保同一时刻仅有一个任务执行。如果应用中有多个类似的任务,通常需要重复维护这种逻辑,尤其当业务规模扩大到整个公司时,效率会变得低下。
此外以下需求也难以满足:
1、不支持动态调整任务参数,例如某类任务需要根据特定参数执行定制化操作。
2、不支持任务分片处理,尤其在处理海量数据时,多个系统之间的协同非常复杂。
3、难以实现失败重试或任务转移。例如,执行失败后需要重试,或者将任务转移到其他节点执行。
4、任务执行的数据统计较为困难,无法统计任务的执行时长、失败次数及执行次数等。
5、无法灵活停止任务,例如在机房迁移等特殊场景下需要暂时停止任务。而Cron-Job完美地解决了这些问题,且具备简单的接入方式、极低的延迟和高可靠性,使用起来非常方便。
二、Cron-Job核心优势
1、简单易用
部署与接入极为简便,几步操作即可将应用接入Cron-Job分布式任务调度平台。
支持任意Cron表达式的任务调度,提供常见的分片与随机调度策略;支持失败丢弃、失败重试等失败策略;支持动态调整任务参数。
2、毫秒级调度延迟
采用提前调度策略,优化服务器时间差、网络延迟等因素,确保任务执行更为及时,实际测试延迟为个位数毫秒级(考虑到执行器的GC和性能)。
基于ACK确认、失败重试、过期调度等策略,保证任务执行的准确性与可靠性。当然,也可以配置为禁用失败重试。
3、多租户支持
对于多业务线的企业,Cron-Job仅需部署一套任务调度平台,即可支撑多个业务线接入,并保证业务线之间的权限互不干扰。
4、用户体验与效率
用户接入更加简便,只需在代码中添加注解,系统会自动注册,免去额外配置。只需要点击启动即可。
管理后台UI设计精美简洁,优化了交互逻辑,使用更加直观与高效。
官方文档结构清晰,覆盖全面,帮助用户快速理解和自主接入、部署。
5、增强的横向扩展能力
支持多租户接入,单个平台即可支撑企业内部所有业务线。
支持集群多节点部署,具备优异的横向扩展能力,可以应对更高并发和更大规模的任务量。
6、更安全的管理后台
精细化的权限管理功能,支持接口级别的权限控制,保障任务的安全性。同时,实现了租户间的权限隔离。
后台管理接口、执行器与调度器之间的OpenAPI通信,采用签名校验机制,防止参数篡改、伪造与重放等安全问题。
7、更高效的问题排查
管理后台提供多维度日志详情,帮助用户全面了解任务执行情况,便于高效排查问题。
三、Cron-Job架构
1、模块架构

2、部署架构

四、原生Java接入
1、引入maven依赖
https://mvnrepository.com/artifact/cn.horace.cronjob/cronjob-executor
<groupId>cn.horace.cronjob</groupId>
<artifactId>cronjob-executor</artifactId>
<!--
同时提供有Gradle等各类依赖类型
https://mvnrepository.com/artifact/cn.horace.cronjob/cronjob-executor
-->
<dependency>
<groupId>cn.horace.cronjob</groupId>
<artifactId>cronjob-executor</artifactId>
<version>最新版本</version>
</dependency>
<!--
同时提供有Gradle等各类依赖类型
https://mvnrepository.com/artifact/cn.horace.cronjob/cronjob-executor
-->
<dependency>
<groupId>cn.horace.cronjob</groupId>
<artifactId>cronjob-executor</artifactId>
<version>最新版本</version>
</dependency>
2、实现任务处理接口
* Created in 2025-01-01 10:44.
@TaskConfig(name = "普通测试任务", cron = "* * * * * ? ", routerStrategy = RouterStrategy.RANDOM)
public class DemoCronTask implements TaskHandler {
private static final Logger logger = LoggerFactory.getLogger(DemoCronTask.class);
* @return 任务执行结果,如果执行成功,则返回HandlerResult.success(),如果执行失败,则返回HandlerResult.fail(),返回null,也判定是失败
public HandlerResult handle(TaskParams params) {
logger.info("task handler..., params:{}", params);
Random random = new Random();
int delay = random.nextInt(50);
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(delay));
return HandlerResult.success();
/**
* 演示任务
* Created in 2025-01-01 10:44.
*
* @author Horace
*/
@TaskConfig(name = "普通测试任务", cron = "* * * * * ? ", routerStrategy = RouterStrategy.RANDOM)
public class DemoCronTask implements TaskHandler {
private static final Logger logger = LoggerFactory.getLogger(DemoCronTask.class);
/**
* 执行任务的方法
*
* @param params 任务参数
* @return 任务执行结果,如果执行成功,则返回HandlerResult.success(),如果执行失败,则返回HandlerResult.fail(),返回null,也判定是失败
*/
@Override
public HandlerResult handle(TaskParams params) {
logger.info("task handler..., params:{}", params);
Random random = new Random();
int delay = random.nextInt(50);
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(delay));
return HandlerResult.success();
}
}
/**
* 演示任务
* Created in 2025-01-01 10:44.
*
* @author Horace
*/
@TaskConfig(name = "普通测试任务", cron = "* * * * * ? ", routerStrategy = RouterStrategy.RANDOM)
public class DemoCronTask implements TaskHandler {
private static final Logger logger = LoggerFactory.getLogger(DemoCronTask.class);
/**
* 执行任务的方法
*
* @param params 任务参数
* @return 任务执行结果,如果执行成功,则返回HandlerResult.success(),如果执行失败,则返回HandlerResult.fail(),返回null,也判定是失败
*/
@Override
public HandlerResult handle(TaskParams params) {
logger.info("task handler..., params:{}", params);
Random random = new Random();
int delay = random.nextInt(50);
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(delay));
return HandlerResult.success();
}
}
3、新建启动类
public class ExampleExecutor {
private static final Logger logger = LoggerFactory.getLogger(ExampleExecutor.class);
public static void main(String[] args) {
ArrayList<Object> taskObjects = new ArrayList<>();
taskObjects.add(new DemoCronTask());
ExecutorConfig config = ExecutorConfig.Builder.newBuilder(taskObjects)
.address("http://127.0.0.1:9527")
.appName("example-executor")
.signKey("7d890a079948b196756rtf5452d2245t")
CronJobExecutorClient.init(config).start();
/**
* 示例执行器
* <p>
* * @author Horace
*/
public class ExampleExecutor {
private static final Logger logger = LoggerFactory.getLogger(ExampleExecutor.class);
public static void main(String[] args) {
ArrayList<Object> taskObjects = new ArrayList<>();
taskObjects.add(new DemoCronTask());
ExecutorConfig config = ExecutorConfig.Builder.newBuilder(taskObjects)
.address("http://127.0.0.1:9527")
.tenant("horace")
.appName("example-executor")
.appDesc("普通示例执行器")
.tag("common")
.signKey("7d890a079948b196756rtf5452d2245t")
.build();
CronJobExecutorClient.init(config).start();
}
}
/**
* 示例执行器
* <p>
* * @author Horace
*/
public class ExampleExecutor {
private static final Logger logger = LoggerFactory.getLogger(ExampleExecutor.class);
public static void main(String[] args) {
ArrayList<Object> taskObjects = new ArrayList<>();
taskObjects.add(new DemoCronTask());
ExecutorConfig config = ExecutorConfig.Builder.newBuilder(taskObjects)
.address("http://127.0.0.1:9527")
.tenant("horace")
.appName("example-executor")
.appDesc("普通示例执行器")
.tag("common")
.signKey("7d890a079948b196756rtf5452d2245t")
.build();
CronJobExecutorClient.init(config).start();
}
}
- address:这里指定的是调度器的地址,如果是本地单节点测试,则指定地址+端口的方式是;如果是线上集群部署,则指定的是Nginx地址;
- tenant:租户的代码,一般是租户的英文名称;
- appName:应用名称,一般是应用的英文名称;
- appDesc:应用描述,应用的中文描述,后续在管理平台中可以看到;
- tag:标签,用于区分同个应用下的不同执行器,如无需求,则不配置即可;
- signKey:签名密钥,用于验证执行器的身份,必须和调度器配置的签名密钥一致。
五、SprintBoot版本接入
1、引入maven依赖
https://mvnrepository.com/artifact/cn.horace.cronjob/cronjob-executor-starter
<groupId>cn.horace.cronjob</groupId>
<artifactId>cronjob-executor-starter</artifactId>
<!--
同时提供有Gradle等各类依赖类型
https://mvnrepository.com/artifact/cn.horace.cronjob/cronjob-executor-starter
-->
<dependency>
<groupId>cn.horace.cronjob</groupId>
<artifactId>cronjob-executor-starter</artifactId>
<version>最新版本</version>
</dependency>
<!--
同时提供有Gradle等各类依赖类型
https://mvnrepository.com/artifact/cn.horace.cronjob/cronjob-executor-starter
-->
<dependency>
<groupId>cn.horace.cronjob</groupId>
<artifactId>cronjob-executor-starter</artifactId>
<version>最新版本</version>
</dependency>
2、实现任务处理接口
@TaskConfig(name = "Spring测试任务", cron = "* * * * * ? ", routerStrategy = RouterStrategy.RANDOM)
public class DemoCronTask implements TaskHandler {
private static final Logger logger = LoggerFactory.getLogger(DemoCronTask.class);
* @return 任务执行结果,如果执行成功,则返回HandlerResult.success(),如果执行失败,则返回HandlerResult.fail(),返回null,也判定是失败
public HandlerResult handle(TaskParams params) {
logger.info("task handler..., params:{}", params);
Random random = new Random();
int delay = random.nextInt(200);
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(delay));
return HandlerResult.success();
/**
* 演示任务
*
* @author Horace
*/
@Component
@TaskConfig(name = "Spring测试任务", cron = "* * * * * ? ", routerStrategy = RouterStrategy.RANDOM)
public class DemoCronTask implements TaskHandler {
private static final Logger logger = LoggerFactory.getLogger(DemoCronTask.class);
/**
* 执行任务的方法
*
* @param params 任务参数
* @return 任务执行结果,如果执行成功,则返回HandlerResult.success(),如果执行失败,则返回HandlerResult.fail(),返回null,也判定是失败
*/
@Override
public HandlerResult handle(TaskParams params) {
logger.info("task handler..., params:{}", params);
Random random = new Random();
int delay = random.nextInt(200);
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(delay));
return HandlerResult.success();
}
}
/**
* 演示任务
*
* @author Horace
*/
@Component
@TaskConfig(name = "Spring测试任务", cron = "* * * * * ? ", routerStrategy = RouterStrategy.RANDOM)
public class DemoCronTask implements TaskHandler {
private static final Logger logger = LoggerFactory.getLogger(DemoCronTask.class);
/**
* 执行任务的方法
*
* @param params 任务参数
* @return 任务执行结果,如果执行成功,则返回HandlerResult.success(),如果执行失败,则返回HandlerResult.fail(),返回null,也判定是失败
*/
@Override
public HandlerResult handle(TaskParams params) {
logger.info("task handler..., params:{}", params);
Random random = new Random();
int delay = random.nextInt(200);
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(delay));
return HandlerResult.success();
}
}
注意:这里的任务类一定要打上Spring的注解,可以是@Component,把对象实例交给Spring管理。
3、新建配置类
* Created in 2025-01-01 21:00.
private static final Logger logger = LoggerFactory.getLogger(AppConfig.class);
public ExecutorStarterConfig cronJobExecutorStarterConfig() {
ExecutorStarterConfig config = new ExecutorStarterConfig();
config.setAddress("http://127.0.0.1:9527");
config.setTenant("horace");
config.setAppName("example-executor-starter");
config.setAppDesc("Spring示例执行器");
config.setSignKey("7d890a079948b196756rtf5452d2245t");
/**
* Created in 2025-01-01 21:00.
*
* @author Horace
*/
@Configuration
public class AppConfig {
private static final Logger logger = LoggerFactory.getLogger(AppConfig.class);
/**
* 自定义配置
*
* @return
*/
@Bean
public ExecutorStarterConfig cronJobExecutorStarterConfig() {
ExecutorStarterConfig config = new ExecutorStarterConfig();
config.setAddress("http://127.0.0.1:9527");
config.setTenant("horace");
config.setAppName("example-executor-starter");
config.setAppDesc("Spring示例执行器");
config.setTag("common");
config.setSignKey("7d890a079948b196756rtf5452d2245t");
return config;
}
}
/**
* Created in 2025-01-01 21:00.
*
* @author Horace
*/
@Configuration
public class AppConfig {
private static final Logger logger = LoggerFactory.getLogger(AppConfig.class);
/**
* 自定义配置
*
* @return
*/
@Bean
public ExecutorStarterConfig cronJobExecutorStarterConfig() {
ExecutorStarterConfig config = new ExecutorStarterConfig();
config.setAddress("http://127.0.0.1:9527");
config.setTenant("horace");
config.setAppName("example-executor-starter");
config.setAppDesc("Spring示例执行器");
config.setTag("common");
config.setSignKey("7d890a079948b196756rtf5452d2245t");
return config;
}
}
4、新建启动类
* Created in 2025-01-01 21:00.
public class ExampleSpringExecutor {
private static final Logger logger = LoggerFactory.getLogger(ExampleSpringExecutor.class);
public static void main(String[] args) {
SpringApplication.run(ExampleSpringExecutor.class, args);
/**
* Created in 2025-01-01 21:00.
*
* @author Horace
*/
@EnableCronJob
@SpringBootApplication
public class ExampleSpringExecutor {
private static final Logger logger = LoggerFactory.getLogger(ExampleSpringExecutor.class);
public static void main(String[] args) {
SpringApplication.run(ExampleSpringExecutor.class, args);
}
}
/**
* Created in 2025-01-01 21:00.
*
* @author Horace
*/
@EnableCronJob
@SpringBootApplication
public class ExampleSpringExecutor {
private static final Logger logger = LoggerFactory.getLogger(ExampleSpringExecutor.class);
public static void main(String[] args) {
SpringApplication.run(ExampleSpringExecutor.class, args);
}
}
注意:需要在启动类上加上@EnableCronJob注解,开启CronJob的自动配置。
-
广告合作
-
QQ群号:707632017