我写了一个教学型的任务调度系统

1 架构概览

在我的职业生涯里,接触过如下 TimerTask 、Quartz 、SpringTask、 时间轮 HashWheelTimer 、Elastic-Job 、XXL-JOB、PowerJob、AirFlow 等任务调度系统,也曾在一家汽车租赁公司自研过基于 XXL-JOB 改造的任务调度系统。

不少同学对我原来的自研经历很感兴趣,于是我编写了一个教学型的任务调度系统(支持 10万 + 调度任务),希望能帮助中高级工程师快速提升架构思维。

思考了很长时间,好几次都推翻了设计思路,经过多轮思考,架构图如下 :

任务调度系统分为三个核心组件:

1、网关层负责应用的接入,任务的推送。

2、Admin 层负责任务的管理、任务的分片、UI 界面等。

3、Worker 层负责任务的调度,并将任务触发到网关。

之所以这么设计,必须保证所有的组件是可水平扩展的 。

技术栈如下表:

技术名称 简介
Spring Boot 基于 Spring 的快速开发框架,简化配置,提供内嵌服务器和自动配置功能。
MySQL 开源关系型数据库,支持 SQL 和事务,适用于结构化数据存储与管理。
MyBatis 半自动化 ORM 框架,通过 XML/注解灵活映射 SQL 与 Java 对象,支持动态 SQL。
RocksDB 高性能嵌入式 Key-Value 存储引擎,适用于高吞吐、低延迟的本地数据持久化场景。
Netty 异步事件驱动网络框架,支持高并发通信,常用于构建 TCP/UDP/HTTP 协议服务端或客户端。
Guava Google 开发的 Java 核心工具库,提供集合、缓存、字符串处理等高效工具类,简化开发。
Quartz 开源作业调度框架,支持复杂定时任务(如按计划执行、周期性任务)。
Zookeeper 开源分布式协调框架,提供分布式同步、配置管理和服务发现,常用于分布式系统中。

项目仓库截图:

2 部署流程

勇哥将服务部署到 1 台阿里云服务器上,数据库部署在腾讯云。

1 数据库 MySQL 和 Zookeeper

  • 数据库

创建数据库 platform_schedule ,执行 doc 目录下的 SQL 脚本:

执行完成后,如下图:

  • Zookeeper

下载 zookeeper-3.6.0 版本,解压后复制一份 zoo_sample.cfg ,重命名为 zoo.cfg ,保持默认配置即可。

2 部署 Admin、GateWay、Worker

  • 打包

在 Admin 模块、GateWay 模块、Worker 模块执行三个步骤:修改数据库配置、打包、拷贝部署包到服务器。

  • 启动 Admin 服务
bash 复制代码
 nohup java -Xms300m -Xmx350m -jar schedule-admin.jar  &
  • 启动 GateWay 服务
bash 复制代码
 nohup java -Xms200m -Xmx400m -jar schedule-gateway.jar  &
  • 启动 Worker 服务
bash 复制代码
 nohup java -Xms200m -Xmx300m -jar schedule-worker.jar  &

3 线上体验

01 登录

02 集群管理

因为 GateWay 、Woker 都需要向 Admin 注册实例信息,我们可以将 Admin 当做简单的注册中心,所以需要配置如下信息:

  • Admin 注册服务地址 ,格式是:ip1:10001;ip2:10001。
  • zk 集群地址 ,用于 Admin 集群,实现主从模式 。

03 添加应用

我们生产环境创建了1个应用,应用名是: mytest , appKey 为 1400001,客户端访问任务调度系统需要配置 appKey 和 appSecret 。

04 添加任务

我们定义了一个任务 ,名称是:测试任务,每 15 秒执行一次,jobHandler 定义为 myJobHandler 。

05 测试任务

从 gitcode 下载源码后 ,查看 Demo 模块 ,模块需要添加客户端依赖编写任务实现

1、客户端依赖

xml 复制代码
   <!--  依赖 客户端 start -->
  <dependency>
       <groupId>cn.javayong</groupId>
        <artifactId>schedule-client</artifactId>
        <version>${parent.version}</version>
  </dependency>
    <!--  依赖 客户端 end -->

2、配置网关地址以及秘钥

3、编写任务实现

java 复制代码
 @Service
 public class DemoJob {
 ​
     private final static Logger logger = LoggerFactory.getLogger(DemoJob.class);
 ​
     // 1、添加任务调度的自定义注解 RSAnnotation , 值是 job 的 bean值
     // 2、ScheduleParam 调度参数
     // 3、ScheduleResult 调度结果 
     @RSAnnotation(value = "myJobHandler")
     public ScheduleResult doTestJob(ScheduleParam scheduleParam) {
         logger.info("myJobHandler:" + JSON.toJSONString(scheduleParam));
         try {
             Thread.sleep(10000);
         } catch (InterruptedException e) {
         }
         return new ScheduleResult(ScheduleResult.SUCCESS_CODE, "正常响应");
     }
     
 }

4、启动任务演示项目

展示了一个简单的任务调度类 DemoJob,它包含一个可以被调度执行的方法 doTestJob ,该方法的注解是: myJobHandler , 与我们在 Admin 控制台创建的测试任务保持一致。

启动 DemoApplication ,本地执行结果:

如图,每隔 15 秒,我们的控制台会打印执行日志。

在 Admin 调度日志页面,查看日志:

我们也发现启动应用后,可以查看在线应用:

对于客户端来讲,任务执行是网关推送过来的,所以我们在家运行代码也可以执行。网关会保存客户端的出网 IP ,在线应用列表可以展示所有在线客户端。

4 你可以学到什么

1、任务调度专栏

第一期已经开发完成,包含 JDK 常用调度类、Quartz 、Crontab 、ElasticJob、自研 platform-schedule 。

第二期会重点突出 XXL-JOB 的基本原理以及性能瓶颈,以及新一代任务调度系统 PowerJob 的原理(重点 讲解 DAG )。

2、网络编程

我基于 RocketMQ remoting 模块做了些许微调,Admin 服务、Gateway 服务、Worker 服务都是通过 TCP remoting 协议交互。

相比 RocketMQ 复杂的代码,platform-schedule 会简单很多,中高级工程师可以轻松掌握 Netty 编程知识,开阔眼界。

3、组件封装

platform-schedule 每个模块都有不同的组件,比如 worker 模块非常核心的调度组件是依赖 Quartz ,那么如何封装 Quartz 实现灵活的调度呢 ?

同时,Admin 为什么可以知道 Worker 、Gateway 的路由地址,因为它本身是一个简易的注册中心。

如何封装任务调度的 SDK ,并平滑的接入 Spring 生态。

相关推荐
徐小黑ACG7 分钟前
GO语言 使用protobuf
开发语言·后端·golang·protobuf
战族狼魂3 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
杉之4 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
hycccccch5 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq
bobz9655 小时前
k8s 怎么提供虚拟机更好
后端
bobz9656 小时前
nova compute 如何创建 ovs 端口
后端
用键盘当武器的秋刀鱼6 小时前
springBoot统一响应类型3.5.1版本
java·spring boot·后端
Asthenia04127 小时前
从迷宫到公式:为 NFA 构造正规式
后端
Asthenia04127 小时前
像整理玩具一样:DFA 化简和状态等价性
后端
Asthenia04127 小时前
编译原理:打包思维-NFA 怎么变成 DFA
后端