# 分布式集群任务调度系统开发方案

# 一、项目目标

使用quartz实现sp系统中定时任务分布式调度

# 二、整体框架

  1. 整体框架图

picture

  1. 业务流程图/序列图

picture

  1. 数据库中定义好全部定时任务信息
  2. 服务启动会加载该服务对应分组(jobGroup)的所有定时任务,定时任务按规则执行

# 三、复杂功能

  1. 定时任务调度信息管理 a. 定时任务调度信息分页查看

picture

b. 操作说明

picture

  • ① 新建定时任务调度信息
  • ②批量删除调度信息(删除调度信息se_quartz_job,并删除对应调度任务qrtz_开头的表数据)
  • ③清空所有调度任务(qrtz_开头的表中记录清除,任务调度信息se_quartz_job还在)
  • ④清空任务日志表(truncate table se_quartz_job_log; )
  • ⑤立即执行一次调度任务
  • ⑥暂停调度任务(对应任务不再执行)
  • ⑦恢复调度任务(对应任务恢复按时执行)
  • ⑧编辑调度信息(保存后生效)
  • ⑨删除调度信息(删除调度信息se_quartz_job,并删对应调度任务qrtz_开头的表数据)
  1. 定时任务执行日志管理 a. 定时任务执行日志记录分页查看

picture

b. 操作说明

  • ①批量删除任务执行日志记录
  • ②查询(刷新列表数据)
  • ③删除单个日志记录

# 四、实体设计/表设计

  1. quartz相关表quartz.sql
  2. 定时任务调度相关表
-- 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

# 五、接口设计

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

# 调度器配置

# 七、验证

  1. 各服务升级后,启动正常,定时任务执行正常
  2. 测试用例
编号 功能点 前提 步骤 预期结果 自测结果
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