Youlink
YouLink设计的目的是使得Polaris之间的应用可以像函数一样调用并且不需要预先写死代码,使用者可以根据业务需要动态生成函数和执行。下面是主要概念:
执行机构:
- Function 每一步操作的函数,可以是内置的也可以由外部注册。
- Program Funtion集合,负责执行包含的函数以及储存由外部动态输入的变量和执行函数过程过程中产生的上下文。(Context)
- Process Program的集合,执行机构的最大实体。
- Variable 系统中的最小数据存储单位,工作在Function、Program、Process中。
定义机构:
- FunctionTemplate Function的定义模板,负责生成Function。
- ProgramTemplate 存储的Program会在执行时动态Program。
运行:
- Runtime Function的执行逻辑,流程控制
- Launcher Program流程控制
定义
定义机构负责将输入的“代码”保存并在需要的时候生成对应的实体。
Function
function的来源主要有两种,一是内置的函数,例如运算、赋值、改文件名等,而是外部注册。
外部注册Function
{
"name": "YouFileMoveFile",
"template": "HTTPRequestCall",
"option": {
"url": "http://localhost:8300/youlink/movefile"
},
"input": [
{
"name": "source",
"desc": "move from",
"type": "string"
},
{
"name": "target",
"desc": "move to",
"type": "string"
}
],
"output": [
{
"name": "movedPath",
"desc": "new file path",
"type": "string"
}
]
}
这里展示了部分片段关注外部注册,注册时需要最关键的三个要素,基于的函数模板、输入声明、输出声明。
HTTPRequestCall
是进行HTTP接口POST请求的函数模板,在这里可以让运行中的Function调用相应的接口,并且附上了输入和输出的声明。
输入输出声明了函数输入输出的变量的名称,类型,备注。
内置
内置的函数已经继承在代码中,只需要调用,不需要注册。
Program
此部分负责将相应的Program生成相应的程序的部分,如:
{
"body": [
{
"type": "function",
"name": "UpdateDirectory",
"inputs": [
{
"name": "source",
"ref": "youdownloadSavePath"
},
{
"name": "target",
"value": "<to_target>"
}
],
"outputs": [
{
"name": "path",
"assign": "moveToPath"
}
]
},
{
"type": "function",
"name": "YouFileMoveFile",
"inputs": [
{
"name": "source",
"ref": "youdownloadSavePath"
},
{
"name": "target",
"ref": "moveToPath"
}
]
}
]
}
body
中包含了要执行的内容,通过提供的类似AST样式的内容来构建,目前YouLink执行策略是顺序执行,声明中提供了三个最重要的信息:函数名称、输入定义、输出定义,则上面的程序可以解释为:
UpdateDirectory
是要调用的函数,其中需要输入变量中,source取值为储存在Program中的Context变量名为youdownloadSavePath
的值。
{
"name": "source",
"ref": "youdownloadSavePath"
}
不同于大部分编程语言根据类型和位置进行声明,YouLink采用名称的定位法。
另外一个变量target取值为某个用户指定的定值。
{
"name": "target",
"value": "<to_target>"
}
实例中,ref
字段表示引用某一个Context中的变量,value
为用户直接给定的定值。
output则指定了输出的规则,assign将值存入Context中的变量中,name为函数定义时输出的名称(UpdateDirectory定义了一个名为name
的输出)。
{
"name": "path",
"assign": "moveToPath"
}
output不强制取到每一个值,例如:函数A有两个输出a和b,用户只声明了a,则b在执行后被丢弃。
注:后期会提供类似于编程语言一样的方式构建上面的内容
YouFileMoveFile
这个函数提供移动文件的功能,形式上与上文的例子相似,就不再过多的介绍。
整个功能是实现将输入路径的文件移动到相应的位置,其中UpdateDirectory
是预设函数,YouFileMoveFile
为外部服务注册的函数。
执行
执行氛围三个层次,Function、Program、Process,层次由大到小,分别由不同的控制器来控制执行。
Function
function是最小的执行机构,是Program的主要组成部分,Runtime是执行Function的执行控制器。
type Runtime struct {
sync.Mutex
Queue chan *Function
Current *Function
Suspend []*Function
ActiveSignalChan chan *ActiveSignal
}
执行器的规则如下
- 函数拥有三种,初始、挂起、结束
- 函数执行前进入队列等待执行
- 函数立即异步执行并进入挂起
- 当函数完成后使用ActiveSignal将函数改变为结束
for {
select {
case function := <-r.Queue:
r.Lock()
function.Id = xid.New().String()
r.Suspend = append(r.Suspend, function)
r.Unlock()
go func() {
function.OnRun(function, r)
}()
case active := <-r.ActiveSignalChan:
function := r.GetSuspendFunction(active.Id)
if function.OnActive != nil {
function.OnActive(active, function, r)
}
r.RemoveSuspendFunction(active.Id)
function.OnDone <- struct{}{}
case <-ctx.Done():
return
}
}
ActiveSignal
ActiveSignal是改变函数执行状态的方法,该方法会将执行的结果一并返回,Function由挂起状态走向结束状态。
ActiveSignal在实际的使用过程中可能来自外部,例如HTTP请求外部服务执行后,由外部再调用回掉接口输入信号。也可以由内部,例如内置函数调用。
Program
ProgramLauncher
和Runtime
有着相似的结构以及规则,不一样的是,不需要ActiveSignal
,只需要保证Program
内部的Function
的执行顺序即可。
对于Program来说,执行完的结果会根据用户的定义Variable
放进Program
的Context
中,类似于局部变量的机制。
type ProgramLauncher struct {
sync.Mutex
Suspend []*Program
Queue chan *Program
}
Process
ProcessManager 负责管理Process状态,支持多个Process同时执行。