OUTLINE
@[TOC]
# 定时任务服务模块
### 需求背景
* 当前后台CPP服务存在大量的定时任务,造成各服务代码复杂化。
* 各个服务的各个定时任务配置各不相同,但逻辑基本一致。
* 定时任务出现问题时难以排查,需要开发者日志大量埋点事后分析,运维人员难以介入。
* 调试时定时任务困难,需要编写额外的代码或者特殊的配置进行调试,无法快速手动触发定时任务配合测试。
* 无法统一管理整体系统的定时任务,开发、部署配置、分析调试、优化都需要分别对待,十分麻烦。
### 运行基本流程
```mermaid
sequenceDiagram
title: 定时任务基本流程
participant 定时任务服务
participant 目标服务(1)
定时任务服务->>定时任务服务:自身定时器判断是否触发任务
定时任务服务->>目标服务(1):触发事件通知(事件ID,触发类型,触发ID)
目标服务(1)-->>定时任务服务:响应已接收触发通知
目标服务(1)->>目标服务(1):执行对应触发事件。
目标服务(1)->>定时任务服务:事件执行信息。
定时任务服务-->>目标服务(1):事件执行信息已存储完毕
```
### 概念解释
###### 任务(TASK)
任务即计划任务,用户通过配置任务用来执行某些定时业务或者特定触发执行的业务,定时任务服务主要管理的就是各个任务。
每个任务由用户设置唯一的任务ID(taskid),该任务ID将供下游的业务服务识别并执行相关业务操作。
每个任务支持3种状态来标记其生命周期,分别为:
* 正常运行,正常运行中任务会检查定时和日期时间触发。也支持手动触发执行
* 停止,定时触发功能关闭,但是支持手动触发
* 关闭,禁止所有触发功能。
任务配置支持:
* 特定日期、特定时间触发执行业务
* 间隔定时触发执行业务
* 手动触发执行业务
###### 目标(TARGET)
目标即任务触发执行的负责业务逻辑的下游服务,用来执行特定的业务操作,每个目标服务都需要实现接收任务触发的接口,
定时任务服务会在合适的时机调用该接口完成定时执行业务。
每个目标都由用户配置唯一的目标ID(targetid),该目标ID用来识别不同的下游服务。
每个目标具有状态来标识自己的声明周期:
* 预设置状态,该状态下,定时任务服务不会尝试连接目标服务,所有触发请求都会被拒接。
* 运行状态,该状态下,定时任务服务主动尝试连接目标服务,如果连接登录成功,触发请求将会被发送给目标服务。
* 暂停,该状态下,定时任务服务主动尝试连接目标服务,但是计划类任务不会被触发,手动触发请求会被发送给目标服务。
###### 关联关系
关联关系即任务和目标建立的关系,一个任务可以绑定多个目标,一个目标也可以被多个任务绑定。
用户通过taskid和targetid来设置关联关系,被绑定到任务上的目标如果状态允许则会执行该任务的触发请求,完成定时业务。
###### 触发响应记录
当某个任务达到触发条件或者手动触发时,将会发送触发请求给目标服务,目标服务可能会出现以下执行状态:
* 拒绝,当目标被设置成预设置或者暂停状态时,会拒绝所有触发或者拒绝定时任务触发。
* 失败,当目标未正常连接或者目标服务接口异常,会显示执行失败信息
* 接受触发开始执行任务,目标正常接收任务触发,并开始执行业务操作。
每个响应记录都会有唯一的triggerid标识,供用户进行定位每次触发事件。触发响应记录也会记录每次触发类型。
触发响应记录标识的是目标服务接受任务调度触发,并不表示其负责的业务执行完成。
###### 任务执行信息
当任务触发请求被发送到目标服务后,目标服务应该立即响应接受触发请求的响应,而具体的业务执行的信息将由另外的接口反馈到
定时任务服务,这些反馈即任务执行信息。
任务执行信息可视为目标服务执行业务的日志,用户通过这些日志来观察具体业务的执行情况。
该信息最重要的部分在于执行状态字段,该字段用来标注完成情况,其中特殊值0标记为业务正常完成。
该字段会为后续形成任务流作为依据。
### 第一阶段任务
* 统一化的定时任务配置和运行逻辑,与具体业务解耦,专注于任务定时执行。
* 支持多种任务定时模式:日期点触发、时间点触发、时间间隔触发、手动触发。
* 支持任务执行情况记录查询,包括
>任务信息:触发方式、是否暂停、接受触发的目标服务。
> 任务执行状态: 触发->通知->完成通知->任务执行信息。
> 任务触发目标:任务->目标服务。
* 动态修改任务定时行为,实现各服务的定时任务的“热更改”。
* 统一化的目标服务配置。
* 支持定时任务和目标服务多对多关系。
* 支持ITP协议、ISC标准模块化,搭载HTTP2ITP模块后支持HTTP协议。
### 接口文档(HTTP)
[接口文档](https://easydoc.xyz/s/74566366)
### 高级功能
###### 任务携带参数(TASK-PARAMETER)
当一些目标执行业务需要额外的参数时,任务触发目标时可以将自己携带的参数一同发送给目标,这样可以为任务带来一定的扩展性,但是相对的,任务跟目标的耦合也会增强,用户再设计任务时应该尽量
避免使用携带参数(或者避免使用负责的任务携带参数)。
任务携带参数格式固定未key-value方式,key和value都为字符串。用户在定时服务设置一连串的参数后,目标服务根据需求读取相关联的key来辅助进行业务逻辑。
当前,任务参数最多支持128个key,并且仅支持内置目标传递任务参数。
###### 内置目标(BUILTIN-TARGET)
内置目标用来执行定时任务服务内部的一些操作,诸如清扫日志,控制任务行为等。一些特殊的内置目标还是其它高级功能的基础组件,比如启动任务流执行的内置目标,停止任务流执行的内置目标等。
###### 任务流(TASKFLOW)
任务流用来定义一连串的互相关联的、有前后顺序关系的任务执行流程。相比于基础任务,任务流可以看做多个基础任务的复合体,任务流的每个基础任务都定义了结果判断与结果处理。除了起始基础任务,每个基础任务都有它的父任务,由父任务的结果判断后选择进行结果处理后来触发下个基础任务,只到执行到最后的基础任务,完成一个负责业务逻辑的执行。
* 任务流由多个基础概念组成,包括:
> * **任务流**,用来标记用户制定的不同任务流。
> * **任务流状态**,任务流状态反应任务流是否处于执行中,如果处于执行中,用户可以强制停止任务流,服务会结束最后一个子任务后直接退出执行,并且执行事件会被标记为中断,并停止接收可能存在的子任务剩余反馈信息。
> * **子任务**,普通的任务的变种,支持用户在上面绑定一个或者多个目标,设置任务携带参数,并且设置该任务执行结果的生成模式。
> * **子任务结果模式**,当子任务绑定的目标返回执行结果后,该配置用来定义子任务此次执行是否成功或者失败,支持定义全部目标执行成功为成功或者有单个目标执行成功即成功这两种模式。
> * **子任务执行超时**,当子任务被触发执行,但是长时间未收集到所有目标的执行结果时,子任务将会把结果定义为超时。
> * **子任务行为**,用来配置子任务收集到结果后根据结果值要触发的下一组子任务。
> * **执行事件**, 任务流每次被触发执行,都将生成一个唯一的执行事件,该事件将记录任务流的执行流程以及被执行到的子任务的执行结果。执行事件拥有成功、失败、超时、中断。
配置任务流步骤:
* 1-配置任务流的ID和描述信息等基础信息
* 2-添加配置任务流所需要用到的所有子任务,每个子任务需要配置和目标的关联关系、结果模式、超时时间、携带参数信息。
* 3-设置任务流的起始子任务,起始子任务最多一个,如果不配置起始子任务,那任务流就无法被调用触发执行。
* 4-设置子任务的结果行为,包括设置结果成功、失败、超时时要执行的子任务。
如果没有设置对应结果的下一步子任务,则任务流执行到该子任务得到对应结果时就会结束执行,完成一次任务流调用。
* 5-完成配置
```mermaid
graph TD;
开始 --> config_flow_basic[配置任务流ID,描述等基础信息]
config_flow_basic --> config_flow_subtask[配置子任务基础信息]
config_flow_subtask --> set_start_subtask[设置起始任务]
set_start_subtask --> set_success[设置成功时任务]
set_start_subtask --> set_faield[设置失败时任务]
set_start_subtask --> set_timeout[设置超时时任务]
set_next_task[设置下一个处理结果的子任务]
set_success --> set_next_task
set_faield --> set_next_task
set_timeout --> set_next_task
set_next_task --> 结束
```
任务流执行流程
```flow
st=>start: 开始
e=>end: 结束
op_excute_flow=>operation: 执行任务流
op_trigger_subtask=>operation: 触发子任务
op_trigger_subtask2=>operation: 触发对应子任务
op_wait_subtask=>operation: 等待子任务的
目标执行完成
op_get_subtaskret=>operation: 分析并获取
子任务执行结果
cond_had_subtask=>condition: 有起始子任务?
cond_had_timeout_sub=>condition: 有超时处理任务?
cond_had_success_sub=>condition: 有成功处理任务?
cond_had_faield_sub=>condition: 有失败处理任务?
cond_subtask_done=>condition: 执行完成?
cond_subtask_success=>condition: 执行成功?
cond_subtask_timeout=>condition: 超时?
cond_had_timeouttask=>condition: 已有超时子任务?
st->op_excute_flow
op_excute_flow->cond_had_subtask
cond_had_subtask(yes)->op_trigger_subtask
op_trigger_subtask->op_wait_subtask
op_wait_subtask->cond_subtask_timeout
cond_subtask_timeout(no)->op_wait_subtask
op_wait_subtask->cond_subtask_done
cond_subtask_done(yes)->op_get_subtaskret
cond_subtask_done(no)->cond_subtask_timeout
cond_subtask_timeout(yes)->cond_had_timeout_sub
cond_had_timeout_sub(yes)->op_trigger_subtask2
op_get_subtaskret->cond_subtask_success
cond_subtask_success(yes)->cond_had_success_sub
cond_had_success_sub(yes)->op_trigger_subtask2
cond_subtask_success(no)->cond_had_faield_sub
cond_had_faield_sub(yes)->op_trigger_subtask2
op_trigger_subtask2->op_trigger_subtask
cond_had_timeout_sub(no)->e
cond_had_success_sub(no)->e
cond_had_faield_sub(no)->e
cond_had_subtask(no)->e
```
* **注意事项**
> * 死循环,如果设置子任务时,某些子任务互相执行可能会造成任务流的死循环,死循环会导致任务流执行无法完成,生成的任务事件过大导致服务性能下降宕机等问题。用户应尽量确保死循环的任务流不被设置。后续将会对任务流执行的深度进行限制,超过限制会强行中断任务流执行过程。
> * 强制终止任务流执行,可能会导致一些子任务的执行信息不回被记录进任务流执行事件中,尽量避免使用需要强制终止任务流的任务流。