Skip to content

任务 - 基础概念

任务概述

任务是流程中一个具体的执行步骤。每个任务包含名称、类型、参数、状态、结果等信息。每个任务会经历以下3个阶段:

  1. 声明: 通过调用任务声明方法为当前流程构建一个任务实例、并将该实例以指定名称保存到流程上下文中。后续可通过任务名进行下一步操作如:初始化参数、获取结果等。声明操作发生在DSL实例构建之后以及执行编排方法start之前。调用声明方法时通常会传入一个闭包做为参数,该闭包中包含该任务的初始化逻辑。
  2. 初始化:完成任务的参数初始化操作,通常是执行任务声明时传入的闭包。初始化发生在调用任务指令之后以及任务执行之前。当初始化完成后任务的参数以及依赖都会准备好。
  3. 执行:执行具体的任务,并将结果保存在任务的result字段中,如果出现错误对应异常会保存在任务的runtimeError字段中。

任务声明方式

任务声明支持以下3种方式:

  1. 基础声明:即工作流提供的基础任务务如CODEHTTPBOOLE等。普通任务通过调用DSL实例的任务方法即可声明、这些任务声明方法均为大写。
  2. app声明:须在config.groovy中配置应用后才可以声明,并通过调用应用实例的任务方法进行声明。
  3. 模板函数声明:对于相似度很高的任务,可以封装在模板函数中,然后通过调用模板函数进行声明。

基础任务声明

DSL文件中可直接声明基础任务,其语法格式如下:

任务名 = 任务类型 {
  // 任务参数初始化逻辑
}
  • 任务名:任务名称必须唯一且符合groovy的变量命名规范,且不能与隐式变量名冲突,可以采用中文但不能用特殊符号。
  • 任务类型:目前支持基础任务类型有CODE、HTTP、BOOLE、EACH、ABORT、CASE。所有基础任务类型均为大写字母。
  • 闭包参数:任务类型后面的一对大括号为Groovy中闭包,任务初始化逻辑写入其中。

声明逻辑

任务类型本质上对应DSL实例中的一个声明方法,参数通常为一个闭包,其声明逻辑如下:

  1. dsl脚本调用声明方法,并传入一个闭包
  2. 声明方法的内部将创建一个相关任务实例,并将任务实例设置为闭包参数的Delegate(委派者),这样在闭包中才可以访问任务实例的属性,从而初始化任务参数。
  3. 将闭包直接返回并以任务名为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代码,产生结果将直接作为任务结果。
  • HTTP url为HTTP任务属性,并且闭包的Delegate(委派者)为HTTP任务实例,所以可以直接对其赋值。
  • task1.result.word:task1是一个委派了CodeTask实例的闭包,基于Groovy闭包的委派者机制其任务内部属性可直接获取。

app 任务声明

app任务声明语法:

任务名 = 应用实例名.任务方法 {
	  // 任务初始化逻辑
}
  • 任务名:任务名称必须唯一且符合groovy的变量命名规范,且不能与隐式变量名冲突,可以采用中文但不能用特殊符号。
  • 应用实例名:在config.groovy中配置应用时对应的名称。
  • 任务方法:应用实例中支持的任务方法,如果方法名为defaultTask可直接省略
  • 闭包参数:任务类型后面的一对大括号为Groovy中闭包,任务初始化逻辑写入其中。

app任务声明过程

  1. dsl脚本调用app应用声明方法,并传入一个闭包
  2. 声明方法的内部将创建一个相关任务实例,并将任务实例设置为闭包参数的Delegate(委派者),这样在闭包中才可以访问任务实例的属性,从而初始化任务参数。
  3. 将闭包直接返回并以任务名为key保存在DSL实例的binding(上下文变量)中,后续dsl中可通过任务名直接获取任务实例和属性。

MySql 任务声明示例

  1. 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密码
  1. 在dsl文件中声明sql语句应用
sqlTask1 = mysql.defaultTask {
  sql="select * from users where id=${input.id}"
}
  • mysql 为配置的应用实例名。
  • defaultTask 任务声明方法, defaultTask为应用默认任务方法可以直接省略。
  • sqlSqlTask中的一个属性,基于闭包的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"

快捷声明流程:

  1. 调用dsl脚本实例的HTTP方法并传入一个固定参数值
  2. 创建一个httpTask实例,创建一个闭包以对应任务实例做为Delegate(委派者),新建闭包中调用httpTask的quickInitialization方法。
  3. HttpTask#quickInitialization方法内部进行初始化填充url与method属性
  4. 返回闭包并以任务名为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流程中使用最频繁的一个属性。

下一步