开源源码,项目下载地址https://gitee.com/lowcode2/ailowcode.git
分布式任务调度
本系统使用的是基于Quartz技术实现的分布式任务调度。基于Quartz实现的分布式任务调度是一种高效的任务管理方案。它利用Quartz的定时任务功能,结合分布式架构,实现任务的分布式执行与调度。这种方案提高了任务处理的并发性和可靠性,适用于大规模、高复杂度的任务调度场景
(一)操作手册
点击菜单“无代码配置/任务调度/管理”,打开任务调度管理页面。如下图所示。
点击任务调度管理页面工具栏的“添加”按钮,打开新增对话框,创建一条新的定时任务,如下⁄图所示。
任务调度表单中的类方法/请求地址,支持模式说明如下表:
请求地址 | 功能说明 |
service类方法 | 案例:定时执行指定的业务service方法 方法:tmptTimerLogService.selectById() 参数:{"id":"1"} |
存储过程 | 案例:call event_daily_report |
http请求 | 案例:定时执行指定无代码任务流 地址:http://lowcode-core/core/coding/dynamicTaskflow/innerService/executeTask 参数:{"taskFlowId":"1849783958267596802"};taskFlowId是任务流的ID |
任务调度表单中的cron表达式:cron表达式是一个用于设置定时任务的字符串,它由6个时间元素组成,分别表示秒、分、时、日、月和周。每个时间元素可以使用特定的值、范围或通配符来表示不同的时间规则。例如,0 0 1 * * ?表示每天凌晨1点执行任务。
cron表达式的语法格式可以总结为:秒 分 时 日 月 周。每个时间元素的取值范围和特殊字符如下:
秒(0-59):表示执行任务的具体秒数。
分(0-59):表示执行任务的具体分钟数。
时(0-23):表示执行任务的具体小时数。
日(1-31):表示执行任务的具体日期。
月(1-12):表示执行任务的具体月份。
周(0-7):表示执行任务的具体星期几,其中0和7都表示星期天。
cron表达式的特殊字符包括:
*:表示任意值。
/:表示步长。
L:表示最后一天。
W:表示最近的工作日。
启动/停止定时任务:点击定时任务表单的“启动定时器”按钮来启动定时器。点击定时任务表单的“停止定时器”按钮来停止定时器。点击“任务调度管理”列表页面的“执行一次”按钮,只执行一次任务,方便测试验证。
任务调度日志:点击菜单“无代码配置/任务调度/日志”,打开任务调度日志页面,如下图所示。
(二)设计研发
1. 需求分析
本系统使用的是基于Quartz技术实现的分布式任务调度。
- 任务调度管理:设计个任务调度管理页面,用来管理系统中所有的定时任务(创建、编辑、删除等)。
- 任务创建:调用方式:service方法、调用http接口、调用数据库存储过程3种方式。执行时间:通过CRON时间表达式指定。
- 任务执行:定时任务支持操作:启动任务、停止任务、执行一次任务。
- 任务执行日志:记录定时任务执行的日志。
2. 总体设计
任务调度子系统应用技术:分布式任务调度quartz技术。
任务高度子系统由任务调度管理、任务执行、任务执行日志3部分组成。功能结构如下图所示。
3. 概要设计
1) 用例设计
任务调度子系统用例分析,请看下表.
用例:任务调度系统用例 范围:无代码-任务调度 级别:管理员 主要参与者:管理员 涉众及其关注点: 管理员:
前置条件:无。 成功保证:无。 主成功场景:
扩展:无 特殊需求:无 技术与数据变元表:无 发生频率:无。 未解决问题:无 |
2) 用例图设计
任务调度用例图。该系统面向管理员。管理员可以创建调度任务、启动定时任务、停止定时任务、执行一次定时任务等操作,定时任务执行状态作为日志信息记录在日志表。用例图如下图所示。
4. 详细设计
本系统使用的是基于Quartz技术实现的分布式任务调度。首先,在pom.xml中导入依赖包如下所示。
1. <dependency>
2. <groupId>org.springframework.boot</groupId>
3. <artifactId>spring-boot-starter-quartz</artifactId>
4. <version>2.3.12.RELEASE</version>
5. </dependency>
1) 数据库表设计
调度任务管理表,主要是对调度任务进行增删改查,及任务的状态(启动、停止)管理等操作,表名:tmpt_timer。数据表结构信息,如下表所示。
序号 | 列名 | 数据类型 | 主键 | 非空 | 说明 |
1 | id | bigint | 是 | 是 | 主键 |
2 | name | varchar(100) | 是 | 名称; | |
3 | description | varchar(100) | 是 | 描述; | |
4 | class_name | varchar(100) | 是 | 类名、http地址; | |
5 | param | varchar(1000) | 是 | 请求参数; | |
6 | expression | varchar(100) | 是 | cron表达式; | |
7 | timer_is_activity | varchar(100) | 是 | 定时任务状态; | |
8 | deleteflag | int | 是 | 是否删除;模板:逻辑删除标志:1:true,0:false | |
9 | http_method | varchar(10) | http请求方式(post/get); | ||
10 | 创建人等字段省略 |
调度任务日志表,主要是记录任务调度的起始时间、结束时间、响应结果等内容。表名:tmpt_timer_log。数据表结构信息,如下表所示。
序号 | 列名 | 数据类型 | 主键 | 非空 | 说明 |
1 | id | bigint | 是 | 是 | 主键 |
2 | start_time | datetime | 是 | 开始时间; | |
3 | end_time | datetime | 是 | 结束时间; | |
4 | result | varchar(100) | 是 | 结果; | |
5 | tmpt_timer_id | varchar(1000) | 是 | 调度任务管理表外键; | |
6 | deleteflag | int | 是 | 是否删除;模板:逻辑删除标志:1:true,0:false | |
7 | 创建人等字段省略 |
5. 代码解读:任务调度
调度任务后端代码设计,用到设计模式中的工厂模式、模板模式和策略模式,类图设计如下。
任务调度模块的包名com.rt.base.modules.timerjob。项目启动时,执行配置类config.SchedulerConfig,会加载配置类目录下的配置文件spring-quartz.properties。其中job.jobStrategy.strategy包中存放的是任务执行策略类,包的目录结构如下所示。
任务调度管理列表中,点击定时任务“执行一次”调用方法如下。通过任务工厂类获取Job策略类,然后执行策略模板方法execute(),模板方法execute()中再调用各自Job策略的job()方法。
1. public synchronized Result<TmptTimerBizDto> executeOnce(TmptTimerBizDto tmptTimerBizDto) {
2. // 执行Job策略
3. JobStrategy jobStrategy = JobStrategyFactory.getJobStrategy(tmptTimerBizDto);
4. return jobStrategy.execute();
5. }
JobStrategyFactory中获取JobStrategy时,是通过请求地址分析出请求的类型,比如:请求地址以“call ”开头,返回存储过程调用类型;请求地址以“()”结尾,返回服务类方法调用类型;请求地址以“http://”或“https://”开头,返回http请求调用类型。然后,根据不同的返回类型,使用不同的策略。如下所示。
1. public class JobStrategyFactory {
2. public static JobStrategy getJobStrategy(TmptTimerBizDto tmptTimerBizDto) {
3. String className = tmptTimerBizDto.getClassName();
4. Result typeResult = isClassInstance(className);
5. String type = (String) typeResult.getMap().get("type");
6. JobStrategy jobStrategy = null;
7. //存储过程
8. if (type.equalsIgnoreCase("1")) {
9. jobStrategy = new ProcedureJobStrategy(tmptTimerBizDto, typeResult);
10. }
11. // spring 方法
12. else if (type.equalsIgnoreCase("2")) {
13. jobStrategy = new SpringBeanJobStrategy(tmptTimerBizDto, typeResult);
14. }
15. // remote invoke
16. else if (type.equalsIgnoreCase("3")) {
17. jobStrategy = new HttpRemoteJobStrategy(tmptTimerBizDto, typeResult);
18. }
19. return jobStrategy;
20. }
21.
22. private static Result isClassInstance(String className) {
23. if (className.startsWith("call ")) {
24. ......
25. map.put("type","1");
26. return Result.builder(map);
27. }
28. else if (className.endsWith("()")) {
29. ......
30. map.put("type","2");
31. return Result.builder(map);
32. }
33. else if(className.startsWith("http://") || className.startsWith("https://")){
34. ......
35. map.put("type","3");
36. return Result.builder(map);
37. }
38. return Result.builder().fault();
39. }
40. ……
JobStrategy是模板类,其中execute()方法是模板方法,此功能是调用job()执行前后,做定时任务执行的日志记录。日志信息记录在tmpt_timer_log表中。模板方法execute()执行代码如下。
6. 系统测试
任务调度子系统测试用例表,如下表所示.
用例编号 | 用例名称 | 测试目的 | 测试步骤 | 预期结果 | 实际结果 | 测试结果 |
1 | 定时任务请求地址是service类方法时,功能测试 | 验证是否执行正常 | 1,打开“无代码配置/任务调度/管理”;2,选择一项任务,点击“编辑”按钮打开对话框;3,配置请求地址为tmptTimerLogService.selectById(),参数为{"id":"1"};4,点击“执行一次” | 响应操作成功 | 响应操作成功 | 通过 |
2 | 定时任务请求地址是存储过程时,功能测试 | 验证是否执行正常 | 1,打开“无代码配置/任务调度/管理”;2,选择一项任务,点击“编辑”按钮打开对话框;3,配置请求地址为call event_daily_report;4,点击“执行一次” | 响应操作成功 | 响应操作成功 | 通过 |
3 | 定时任务请求地址是http请求,功能测试 | 验证是否执行正常 | 1,打开“无代码配置/任务调度/管理”;2,选择一项任务,点击“编辑”按钮打开对话框;3,配置请求地址为http://lowcode-core/...;4,点击“执行一次” | 响应操作成功 | 响应操作成功 | 通过 |
4 | 定时任务请求地址是其它类型,功能测试 | 验证是否执行正常 | 1,打开“无代码配置/任务调度/管理”;2,选择一项任务,点击“编辑”按钮打开对话框;3,配置请求地址为aaaaa;4,点击“执行一次” | 响应: 操作失败:请求地址不支持 | 响应: 操作失败:请求地址不支持 | 通过 |
5 | 省略 |