背景
这几年低代码的概念被说得很多,却不是什么新概念,这本身已经算是历史悠久了。从广义甚至可以说,平时我们做的代码设计:抽象、模块化、链条化、脚本化等,本身就是在"造"一个低代码引擎来降低/减少重复的工作量。
虽然网络上概念火热、讨论众多,但是作为一个后端开发者却没有什么好的选择------90%都是纯表单驱动的搭建工具。而我更想要一个原始的编排引擎,能构建出微服务编排、工作流等:
- 模型驱动为主
- 扩展简单方便
- 可以调试定位
- 只有点和线
实现
我调研了众多的引擎实现方案,发现比较契合的有 conductor 和 temporal 。其中 conductor 是通过 DSL 进行流程编排,而 temporal 则通过写代码进行编排(可以更好地解决流程调试 DEBUG 的问题)。感兴趣的,可以点击链接进行更多地了解。
对于编排方式,秉承(中心化)用户不引入 SDK 、不写代码,可视化编排工具能更好集成的原则,现阶段我更倾向于选择 DSL。
但是在深度研究使用 conductor 后,我发现 conductor 的几个重要缺陷:
- 不支持接口级流程编排(接口级编排恰恰是微服务编排的主要场景:多个接口按组合形成一个新的接口)。接口级是需要乃至毫秒级响应的接口。
- 节点(Task)扩展很麻烦,主逻辑中存在大量具体节点相关的逻辑
- 调度性能差
所以,结合多年的工作流和流程编排经验,我重新实现了 conductor,取名为 Brook。Brook 非常的轻量,不仅支持微服务,还支持应用内逻辑的编排执行(像责任链一样,还可以是动态的)。同时它不仅能中心化部署,还是嵌入到应用中运行。
Brook 仓库地址:github.com/mytang0/bro...
快速使用
为了最大限度地发挥 Brook 引擎的轻量级特性,其核心组件(仅依赖于一些必要的工具包)和使用 SPI(服务提供商接口)的中间件扩展之间进行了刻意的分离。因此,无论应用程序实现框架如何,都可以无缝依赖引擎JAR并初始化相关实例。
不使用 Springboot
xml
<dependencies>
<dependency>
<groupId>xyz.mytang0.brook</groupId>
<artifactId>>brook-engine</artifactId>
<version>${brook.version}</version>
</dependency>
</dependencies>
Springboot(推荐)
xml
<dependencies>
<dependency>
<groupId>xyz.mytang0.brook</groupId>
<artifactId>>brook-spring-boot-starter</artifactId>
<version>${brook.version}</version>
</dependency>
</dependencies>
样例(Demo)
运行
- 克隆项目
bash
git clone https://github.com/mytang0/brook.git
- 导入 IDE
- 运行Demo(brook-demo/brook-demo-spring)
DSL
只有一个 HTTP 节点的流程定义
bash
{
"name": "test-http",
"description": "for showtime",
"taskDefs": [
{
"type": "HTTP",
"name": "test-http",
"input": {
"uri": "http://127.0.0.1:8080/flow/metadata",
"method": "GET",
"params": {
"flowName": "${flow.input.flowName}"
}
}
}
],
"output": "${test-http.output}"
}
SPI
简单的编排节点扩展(Task 扩展)
typescript
package xyz.mytang0.brook.demo.service;
import xyz.mytang0.brook.common.utils.JsonUtils;
import xyz.mytang0.brook.common.utils.TimeUtils;
import xyz.mytang0.brook.spring.boot.annotation.Taskable;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Component
public class DemoService {
@Taskable(type = "test-void", description = "for testing")
public void vd() {
}
@Taskable(type = "test-hello", description = "for testing")
public String hello(String name) {
return "Hello, " + name + " .";
}
@Taskable(type = "test-list", description = "for testing")
public String list(List<Object> list) {
return "list: " + JsonUtils.toJsonString(list);
}
@Taskable(type = "test-array", description = "for testing")
public String array(Object[] array) {
return "array: " + JsonUtils.toJsonString(array);
}
@Taskable(type = "test-map", description = "for testing")
public String map(Map<String, Object> map) {
return "map: " + JsonUtils.toJsonString(map);
}
@Taskable(type = "test-throw-iae", description = "for testing")
public void throwIae() {
throw new IllegalArgumentException();
}
@Taskable(type = "test-timeout", description = "for testing")
public void timeout(Integer sleepSeconds) {
if (sleepSeconds == null) {
sleepSeconds = 5;
}
TimeUtils.sleep(sleepSeconds, TimeUnit.SECONDS);
}
}