任务 - 基础概念
任务概述
任务是流程中一个具体的执行步骤。每个任务包含名称、类型、参数、状态、结果等信息。每个任务会经历以下3个阶段:
- 声明: 通过调用任务声明方法为当前流程构建一个任务实例、并将该实例以指定名称保存到流程上下文中。后续可通过任务名进行下一步操作如:初始化参数、获取结果等。声明操作发生在DSL实例构建之后以及执行编排方法
start之前。调用声明方法时通常会传入一个闭包做为参数,该闭包中包含该任务的初始化逻辑。 - 初始化:完成任务的参数初始化操作,通常是执行任务声明时传入的闭包。初始化发生在调用任务指令之后以及任务执行之前。当初始化完成后任务的参数以及依赖都会准备好。
- 执行:执行具体的任务,并将结果保存在任务的
result字段中,如果出现错误对应异常会保存在任务的runtimeError字段中。
任务声明方式
任务声明支持以下3种方式:
- 基础声明:即工作流提供的基础任务务如
CODE、HTTP、BOOLE等。普通任务通过调用DSL实例的任务方法即可声明、这些任务声明方法均为大写。 - app声明:须在
config.groovy中配置应用后才可以声明,并通过调用应用实例的任务方法进行声明。 - 模板函数声明:对于相似度很高的任务,可以封装在模板函数中,然后通过调用模板函数进行声明。
基础任务声明
DSL文件中可直接声明基础任务,其语法格式如下:
任务名 = 任务类型 {
// 任务参数初始化逻辑
}- 任务名:任务名称必须唯一且符合groovy的变量命名规范,且不能与隐式变量名冲突,可以采用中文但不能用特殊符号。
- 任务类型:目前支持基础任务类型有CODE、HTTP、BOOLE、EACH、ABORT、CASE。所有基础任务类型均为大写字母。
- 闭包参数:任务类型后面的一对大括号为Groovy中闭包,任务初始化逻辑写入其中。
声明逻辑
任务类型本质上对应DSL实例中的一个声明方法,参数通常为一个闭包,其声明逻辑如下:
- dsl脚本调用声明方法,并传入一个闭包
- 声明方法的内部将创建一个相关任务实例,并将任务实例设置为闭包参数的Delegate(委派者),这样在闭包中才可以访问任务实例的属性,从而初始化任务参数。
- 将闭包直接返回并以任务名为key保存在DSL实例的binding(上下文变量)中。
HTTP任务声明方法实现示例
// 基础HTTP任务
public Closure<?> HTTP(Closure<?> closure) {
closure.setDelegate(new HttpTask());
closure.setResolveStrategy(Closure.DELEGATE_FIRST);
return closure;
}任务声明案例
// 声明代码任务
task1 = CODE {
[word: "apiFlow" ] // 设置要查询的词
}
// 声明一个http任务
task2= HTTP {
url = "https://www.baidu.com/s?wd=${task1.result.word}" // 获取tak1的结果 并拼装在url中
}案例说明:
CODE为脚本任务,闭包内部可执行任意Groovy代码,产生结果将直接作为任务结果。HTTPurl为HTTP任务属性,并且闭包的Delegate(委派者)为HTTP任务实例,所以可以直接对其赋值。task1.result.word:task1是一个委派了CodeTask实例的闭包,基于Groovy闭包的委派者机制其任务内部属性可直接获取。
app 任务声明
app任务声明语法:
任务名 = 应用实例名.任务方法 {
// 任务初始化逻辑
}- 任务名:任务名称必须唯一且符合groovy的变量命名规范,且不能与隐式变量名冲突,可以采用中文但不能用特殊符号。
- 应用实例名:在
config.groovy中配置应用时对应的名称。 - 任务方法:应用实例中支持的任务方法,如果方法名为
defaultTask可直接省略 - 闭包参数:任务类型后面的一对大括号为Groovy中闭包,任务初始化逻辑写入其中。
app任务声明过程
- dsl脚本调用app应用声明方法,并传入一个闭包
- 声明方法的内部将创建一个相关任务实例,并将任务实例设置为闭包参数的Delegate(委派者),这样在闭包中才可以访问任务实例的属性,从而初始化任务参数。
- 将闭包直接返回并以任务名为key保存在DSL实例的binding(上下文变量)中,后续dsl中可通过任务名直接获取任务实例和属性。
MySql 任务声明示例
- 在
config.groovy中配置应用
app.mysql.type="org.apiFlow.database.MySqlApp" // mySql应用类名
app.mysql.url="jdbc:mysql://192.168.0.11:3306/wechat_ai" // jdbc url连接
app.mysql.user="root" // mySql用户名
app.mysql.password="123456" // mySql密码- 在dsl文件中声明sql语句应用
sqlTask1 = mysql.defaultTask {
sql="select * from users where id=${input.id}"
}mysql为配置的应用实例名。defaultTask任务声明方法, defaultTask为应用默认任务方法可以直接省略。sql为SqlTask中的一个属性,基于闭包的Delegate(委派者)机制可以直接对其赋值。input.id为流程传入的参数通过input这个隐式对象获取。
注意:上例虽然是拼接sql字符串,但不会有SQL注入风险,因为内部实现会将拼接部分转成
?占位符进行传参。
defaultTask为应用默认任务方法可以直接省略如:
groovy
sqlTask1 = mysql {
sql = "select * from users where id=${input.id}"
}注意:在基础任务中任务方法等同于任务类型,但在app任务中任务方法不等于任务类型,具体类型取决于该方法返回的任务实例。
任务快捷声明
如果任务实现了快捷初始化方法void quickInitialization(Object param) 就可以通过快捷方式进行声明。这种方式可以省略参数闭包直接传入一个具体的值进行初始化。语法如下:
任务名 = 声明方法 参数值HTTP快捷声明示例
httpTask = HTTP "https://www.baidu.com/s?wd=apiFlow"快捷声明流程:
- 调用dsl脚本实例的
HTTP方法并传入一个固定参数值 - 创建一个
httpTask实例,创建一个闭包以对应任务实例做为Delegate(委派者),新建闭包中调用httpTask的quickInitialization方法。 - 在
HttpTask#quickInitialization方法内部进行初始化填充url与method属性 - 返回闭包并以任务名为key保存在DSL实例的binding(上下文变量)中,后续dsl中可通过任务名直接获取任务实例和属性
快捷声明与普通声明最终实例本质上无任何差异,最终上例等同如下:
httpTask = HTTP {
url = "https://www.baidu.com/s?wd=apiFlow"
method = "GET"
}延迟插值
快捷声明如果依赖另一个任务的结果,不能直接拼接嵌入,因为在任务声明阶段该结果并没有产生。如下面dsl将产生错误因为任务声明阶段userTask.result 还没有值
userTask =CODE {
[id:1,name:"小明" ]
}
sqlTask1 = mysql "select * from users where id=${userTask.result.id}"可通过Groovy延迟插值 ${-> 表达式} 方式解决这个问题如:
groovy
userTask =CODE {
[id:1,name:"小明" ]
}
sqlTask1 = mysql "select * from users where id=${-> userTask.result.id}"延迟插值也称闭包插值他不会即时计算表达示的值,而是在任务初始阶段才会最终计算其中的值。
注意:快捷声明任务时由于在DSL初始阶段也会进行任务的声明检测,所以除了引用全局范围的隐式参数外,其它参数都必须采用延迟插值
${-> 表达式}方式进行拼接,否则会出现严重错误。
任务声明的建议与规范
- 最简化原则,尽量采用快捷方式声明,但前提是该任务支持快捷声明。
- 最简化原则,app应用任务尽量采用默认任务以省略声明方法。
- 快捷声明时只支持一个参数,通常是一个简单基础数据类型,拼接字符串时全部采用延迟插值
${-> 表达式}方式处理以免造成无法加载dsl的错误。
任务基本属性
在ApiFlow中所有任务的基类是AbstractTask 其属性结构如下:
@Getter
@Setter
public abstract class AbstractTask implements Task {
String _type; // 任务类型
String _name; // 任务名称
boolean _async = false; // 是否后台异步执行,默认false
Exception _runtimeError; // 任务执行异常
State _state = State.INITIAL; // 任务状态;
long _useTime = -1; // 用时(毫秒)
final Properties _metaInfo = new Properties(); // 附加的元信息,与执行实际任务无关
final Map<String, ?> vars = new HashMap<>(); // 任务中间变量
Object result; // 任务执行结果
}属性说明:
- 所有基础属性都可通过任务实例访问。
- 下划线开头的属性表示内部属性是只读的不能在dsl中修改,包括其属性内部的值。
vars是一个特殊属性它可用于存储任务初始化过程中的一些变量,当任务不符预期时可通过该变量观察任务初始时中间结果。result存储任务结果,是dsl流程中使用最频繁的一个属性。