# 分布式集群任务调度系统开发方案
# 一、项目目标
使用quartz实现sp系统中定时任务分布式调度
# 二、整体框架
- 整体框架图
- 业务流程图/序列图
- 数据库中定义好全部定时任务信息
- 服务启动会加载该服务对应分组(jobGroup)的所有定时任务,定时任务按规则执行
# 三、复杂功能
- 定时任务调度信息管理 a. 定时任务调度信息分页查看
b. 操作说明
- ① 新建定时任务调度信息
- ②批量删除调度信息(删除调度信息se_quartz_job,并删除对应调度任务qrtz_开头的表数据)
- ③清空所有调度任务(qrtz_开头的表中记录清除,任务调度信息se_quartz_job还在)
- ④清空任务日志表(truncate table se_quartz_job_log; )
- ⑤立即执行一次调度任务
- ⑥暂停调度任务(对应任务不再执行)
- ⑦恢复调度任务(对应任务恢复按时执行)
- ⑧编辑调度信息(保存后生效)
- ⑨删除调度信息(删除调度信息se_quartz_job,并删对应调度任务qrtz_开头的表数据)
- 定时任务执行日志管理 a. 定时任务执行日志记录分页查看
b. 操作说明
- ①批量删除任务执行日志记录
- ②查询(刷新列表数据)
- ③删除单个日志记录
# 四、实体设计/表设计
- quartz相关表quartz.sql
- 定时任务调度相关表
-- 1. 定时任务调度表
-- ----------------------------
-- 定时任务调度表
-- ----------------------------
drop table if exists se_quartz_job;
create table se_quartz_job (
job_id bigint(20) not null auto_increment comment '任务ID',
job_name varchar(64) default '' comment '任务名称',
job_group varchar(64) default 'DEFAULT' comment '任务组名',
invoke_target varchar(500) not null comment '调用目标字符串',
cron_expression varchar(255) default '' comment 'cron执行表达式',
misfire_policy varchar(20) default '3' comment '计划执行错误策略(1立即执行 2执行一次 3放弃执行)',
concurrent char(1) default '1' comment '是否并发执行(0允许 1禁止)',
status char(1) default '0' comment '状态(0正常 1暂停)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default '' comment '备注信息',
primary key (job_id, job_name, job_group)
) engine=innodb auto_increment=100 comment = '定时任务调度表';
-- 2. 初始化定时任务调度信息
INSERT INTO `se_quartz_job`
(`job_name`, `job_group`, `invoke_target`, `cron_expression`, `misfire_policy`, `concurrent`, `status`, `create_by`, `remark`) VALUES
('加载角色审核信息', 'audit_service', 'loadRoleScheduler.loadRole', '0 */1 * * * ?', '3', '1', '0', 'system', '加载角色审核信息'),
('执行发布任务', 'audit_service', 'publishService.loadTasks', '*/10 * * * * ? ', '3', '1', '0', 'system', ''),
('检查超时分发任务', 'dispatch', 'checkDispatchTask.checkTimeOut', '0 */10 * * * ? ', '3', '1', '0', 'system', ''),
('处理分发请求任务', 'dispatch', 'executeDispatchRequest.execute', '0 */1 * * * ? ', '3', '1', '0', 'system', ''),
('处理频道、节目单分发请求任务', 'dispatch', 'executeDispatchRequest.dispatchChannelSchedules', '0 */1 * * * ? ', '3', '1', '0', 'system', ''),
('响应分发请求', 'dispatch', 'executeDispatchRequest.executeNotify', '0 */1 * * * ? ', '3', '1', '0', 'system', ''),
('执行分发任务', 'dispatch', 'executeDispatchTask.executeDispatchTask', '0 */1 * * * ? ', '3', '1', '0', 'system', ''),
('执行频道、节目单分发任务', 'dispatch', 'executeDispatchTask.executeDirectDispatchTask', '0 */1 * * * ? ', '3', '1', '0', 'system', ''),
('1分钟内没有收到mq异步回调,则重新投递.', 'dispatch', 'executeRetryMqCollect.execute', '0 */1 * * * ? ', '3', '1', '0', 'system', '1分钟内没有收到mq异步回调,则重新投递.'),
('检查内容重复任务', 'cms', 'repeatContentService.repeatTask', '0 */30 * * * ? ', '3', '1', '1', 'system', ''),
('执行离线导入任务', 'cms', 'offlineImportService.executeImportRecord', '0 */1 * * * ?', '3', '1', '0', 'system', ''),
-- chances.cms.cast.task.open
('演职员同步任务', 'cms', 'castPublishTask.execute', '0 */5 * * * ?', '3', '1', '0', 'system', ''),
('自动栏目自动编排任务', 'cms', 'categoryAutoArrangeTask.execute', '0 */1 * * * ?', '3', '1', '0', 'system', ''),
-- chances.cms.category.dispatch-switch
('厂商栏目编排单自动分发任务', 'cms', 'categoryItemDispatchTask.execute', '0 0/5 * * * ?', '3', '1', '0', 'system', ''),
-- chances.cms.category.publish-switch
('自动栏目编排单自动送审任务', 'cms', 'categoryItemPublishTask.execute', '0 0/5 * * * ?', '3', '1', '0', 'system', ''),
-- CategoryController.changeClearTaskSwitch url:/category/clear/switch/{open}
('EPG栏目编排单清理任务', 'cms', 'clearCategoryItemTask.clearCategoryItem', '0 */5 * * * ?', '3', '1', '0', 'system', ''),
('专栏自动编排任务', 'cms', 'columnAutoArrangeTask.execute', '0 */1 * * * ?', '3', '1', '0', 'system', ''),
('自动专栏编排单自动送审任务', 'cms', 'columnItemPublishTask.execute', '0 0/5 * * * ? ', '3', '1', '0', 'system', ''),
('内容有效期检查任务', 'cms', 'disableExpireContentTask.execute', '0 0 1 * * ?', '3', '1', '0', 'system', ''),
('节目单发布任务', 'cms', 'schedulePublishTask.execute', '20 */10 * * * ?', '3', '1', '0', 'system', ''),
('发送转码任务', 'cms', 'sendTranscodeTask.sendTranscode', '0 */1 * * * ?', '3', '1', '1', 'system', ''),
-- content.export.task.button 0关闭 1开启
('内容导出任务', 'cms', 'statisContentReportTask.execute', '0 0 1 * * ?', '3', '1', '0', 'system', '');
-- 3. 定时任务调度日志表
-- ----------------------------
-- 定时任务调度日志表
-- ----------------------------
drop table if exists se_quartz_job_log;
create table se_quartz_job_log (
job_log_id bigint(20) not null auto_increment comment '任务日志ID',
job_name varchar(64) not null comment '任务名称',
job_group varchar(64) not null comment '任务组名',
invoke_target varchar(500) not null comment '调用目标字符串',
job_message varchar(500) comment '日志信息',
status char(1) default '0' comment '执行状态(2成功 3失败)',
exception_info varchar(2000) default '' comment '异常信息',
primary key (job_log_id)
) engine=innodb comment = '定时任务调度日志表';
-- 4. 定时任务管理相关
-- 1. 菜单表
INSERT INTO `se_sys_menu` (`id`, `code`, `icon`, `name`, `order_no`, `parent_id`, `path`, `title`, `locate_path`, `type`, `hidden`) VALUES (15016, 'quartzMain', NULL, '定时任务', 16, 15, 'quartzMain', 'quartzMain', '#15#15016#', 0, b'0');
-- 2. 枚举表
INSERT INTO `se_sys_enum` (`id`, `name`, `code`, `app_code`, `parent_id`, `order_no`, `keywords`, `type`, `value`, `parent`) VALUES
(1621, '服务名称', 'serverNameEnum', 'sysEnum', NULL, NULL, NULL, 'enum', NULL, NULL),
(1622, '内容管理服务', 'cms', 'sysEnum', 1621, 3, NULL, NULL, NULL, NULL),
(1623, '审核服务', 'audit_service', 'sysEnum', 1621, 1, NULL, NULL, NULL, NULL),
(1624, '分发服务', 'dispatch', 'sysEnum', 1621, 2, NULL, NULL, NULL, NULL);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 五、接口设计
Desc | Method | Url | Body | Response |
---|---|---|---|---|
分页查询定时任务调度信息 | POST | http://IP:port/{server_context}/job/list | { "page": 0, "size": 20, "jobName": "", "jobGroup": "", "keyword": ""} | {"code": 200, "msg": "ok", "items": [], "pageInfo": {}} |
批量删除定时任务调度信息 | DELETE | http://IP:port/{server_context}/job/remove | []// 删除的调度信息id集合 | {"code": 200, "msg": "ok"} |
修改定时任务调度状态 | POST | http://IP:port/{server_context}/job/changeStatus | {// 调度信息"id": 1, "status": 1, ...} | {"code": 200, "msg": "ok", "result": {}} |
任务调度立即执行一次 | POST | http://IP:port/{server_context}/job/run | {// 调度信息"id": 1, "status": 1, ...} | {"code": 200, "msg": "ok", "result": {}} |
新增保存调度 | POST | http://IP:port/{server_context}/job/add | {// 调度信息"id": 1, "status": 1, ...} | {"code": 200, "msg": "ok", "result": {}} |
修改调度 | PUT | http://IP:port/{server_context}/job/edit | {// 调度信息"id": 1, "status": 1, ...} | {"code": 200, "msg": "ok", "result": {}} |
清空调度器的任务 | POST | http://IP:port/{server_context}/job/clean | { "code": 200, "msg": "ok" } | |
校验cron表达式是否有效 | POST | http://IP:port/{server_context}/job/validCron | {// 调度信息"id": 1, "status": 1, "cornExpression": "*/10 * * * * *", ...} | {"code": 200, "msg": "ok", "result": true/false} |
分页查询定时任务执行日志 | POST | http://IP:port/{server_context}/jobLog/list | {"page": 0, "size": 20, "jobName": "", "jobGroup": "", "keyword": ""} | {"code": 200, "msg": "ok", "items": [], "pageInfo": {}} |
批量删除定时任务执行日志 | DELETE | http://IP:port/{server_context}/jobLog/remove | []// 删除的日志id集合 | {"code": 200, "msg": "ok"} |
清空任务日志 | POST | http://IP:port/{server_context}/jobLog/clean | { "code": 200, "msg": "ok"} |
# 六、配置
1. 内容管理服务cms
# 系统名称
chances.system-name: "cms"
# 删除以下定时任务开关配置
chances.cms.cast.task.open: "true/false"
chances.cms.category.dispatch-switch: "true/false"
chances.cms.category.publish-switch: "true/false"
content.export.task.button: "0关闭/1开启"
# 栏目编排单清理任务开关:CategoryController.changeClearTaskSwitch url:/category/clear/switch/{open}
2. 审核发布服务audit_service
# 系统名称
chances.system-name: "audit_service"
3. 分发服务dispatch
# 系统名称
chances.system-name: "dispatch"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 调度器配置
# 七、验证
- 各服务升级后,启动正常,定时任务执行正常
- 测试用例
编号 | 功能点 | 前提 | 步骤 | 预期结果 | 自测结果 |
---|---|---|---|---|---|
quartz-001 | 定时任务调度信息查看 | "1、进入定时任务管理页面 " | 1、定时任务调度信息列表页展示正常 | PASS | |
quartz-002 | 新建定时任务调度信息 | "1、进入定时任务管理页面 2、点击新建按钮 3、未填写必填信息/填写无效的cron表达式 4、点击保存" | 1、定时任务调度信息创建失败,页面展示校验结果 | PASS | |
quartz-003 | "1、进入定时任务管理页面 2、点击新建按钮3、填写必填信息,cron表达式正确,任务状态选暂停 4、点击保存" | "1、定时任务调度信息创建成功 2、定时任务不会按时执行" | PASS | ||
quartz-004 | "1、进入定时任务管理页面2、点击新建按钮 3、填写必填信息,cron表达式正确,任务状态选正常 4、点击保存" | "1、定时任务调度信息创建成功 2、定时任务会按时执行" | PASS | ||
quartz-004 | 修改定时任务调度信息 | "1、进入定时任务管理页面2、选取某定时任务调度信息,点击编辑按钮3、修改信息,未填写必填信息/填写无效的cron表达式4、点击保存" | 1、定时任务调度信息修改失败,页面展示校验结果 | PASS | |
quartz-005 | "1、进入定时任务管理页面2、选取某定时任务调度信息,点击编辑按钮3、修改信息,cron表达式正确,任务状态选暂停4、点击保存" | "1、定时任务调度信息修改成功2、定时任务不会按时执行" | PASS | ||
quartz-006 | "1、进入定时任务管理页面2、选取某定时任务调度信息,点击编辑按钮3、修改信息,cron表达式正确,任务状态选正常4、点击保存" | "1、定时任务调度信息修改成功2、定时任务会按时执行" | PASS | ||
quartz-007 | 立即执行某定时任务 | "1、进入定时任务管理页面2、选取某定时任务调度信息,点击执行按钮" | 1、定时任务执行成功 | PASS | |
quartz-008 | 暂停定时任务 | 定时任务状态为:正常 | "1、进入定时任务管理页面2、选取某定时任务调度信息,点击暂停按钮" | "1、定时任务暂停成功2、定时任务不再执行" | PASS |
quartz-009 | 恢复定时任务 | 定时任务状态为:暂停 | "1、进入定时任务管理页面2、选取某定时任务调度信息,点击恢复按钮" | "1、定时任务恢复成功2、定时任务恢复按时执行" | PASS |
quartz-010 | 清空调度器中的定时任务 | "1、进入定时任务管理页面2、点击‘清空调度任务’操作按钮" | "1、分别对引入quartz的服务发出请求2、定时任务清空成功" | PASS | |
quartz-011 | 删除/批量删除定时任务 | "1、进入定时任务管理页面2、选取某定时任务调度信息,点击删除按钮3、勾选多个定时任务调度信息,点击批量删除按钮" | "1、定时任务调度信息、定时任务删除成功2、删除的定时任务不再执行" | PASS | |
quartz-012 | 查看定时任务执行日志 | "1、进入定时任务管理页面2、选取某定时任务调度信息,双击3、弹窗中点击日志信息tab" | "1、该定时任务的执行日志列表正常展示" | PASS | |
quartz-013 | 删除/批量删除定时任务执行日志 | "1、进入定时任务管理页面2、查看某定时任务的执行日志3、选取某定时任务调度信息,点击删除按钮4、勾选多个定时任务调度信息,点击批量删除按钮" | "1、定时任务执行日志删除成功" | PASS | |
quartz-014 | 清空定时任务执行日志 | "1、进入定时任务管理页面2、点击‘清空任务日志’操作按钮" | "1、定时任务执行日志清除成功 se_quartz_job_log表数据清空(truncate)" | PASS |