如何设计一个工作流系统分享
工作流的应用场景
基于插件化架构的数据处理流水线,专为高效、灵活的数据分析任务设计。
工作流通过标准化接口将数据采集、数据处理、发布等环节解耦为独立插件,支持动态注册与自由编排,实现从数据输入到结果输出的全流程自动化管理。
固定工作流
首先演示一个固定工作流如何实现,工作流通过数组顺序存储插件名,前驱插件的输出作为后续插件的输入,形成链式处理管道。
一、项目文件结构
workflow/
├── main.go # 主程序入口
├── workflows/ # 工作流核心实现
│ ├── data.go # 数据结构定义
│ ├── collector.go # 采集插件
│ ├── processor.go # 数据处理插件
│ └── publisher.go # 发布插件
└── README.md # 使用说明
二、完整代码实现
1. 数据定义 (workflows/data.go
)
package workflows
// 数据实体
type Data struct {
ID string // 数据ID
Value float64 // 数值字段
Text string // 文本字段
ProcessType string // 处理类型标识
}
// 处理类型常量
const (
ProcessClean = "clean" // 数据清洗
ProcessTransform = "transform" // 数据转换
ProcessAnalyze = "analyze" // 数据分析
)
2. 采集插件 (workflows/collector.go
)
package workflows
import "fmt"
// 模拟数据源
var mockData = []Data{
{ID: "D001", Value: 10.5, Text: " Order #123 ", ProcessType: ProcessClean},
{ID: "D002", Value: 25.0, Text: "VIP Customer", ProcessType: ProcessTransform},
{ID: "D003", Value: 99.9, Text: "Urgent!", ProcessType: ProcessAnalyze},
}
// 数据采集插件
func CollectData(outCh chan<- Data) {
for _, data := range mockData {
fmt.Printf("[采集] ID:%s 原始值:%.1f\n", data.ID, data.Value)
outCh <- data
}
close(outCh) // 关闭通道表示采集结束
}
3. 数据处理插件 (workflows/processor.go
)
package workflow
import (
"fmt"
"strings"
)
// 数据处理插件
func ProcessData(inCh <-chan Data, outCh chan<- Data) {
for data := range inCh {
fmt.Printf("[处理] 收到数据:%s 类型:%s\n", data.ID, data.ProcessType)
// 根据类型执行不同处理
switch data.ProcessType {
case ProcessClean:
data.Text = strings.TrimSpace(data.Text) // 清洗空格
case ProcessTransform:
data.Value = data.Value * 1.5 // 数值转换
case ProcessAnalyze:
if data.Value > 50 {
data.Text = "[高价值]" + data.Text // 打标签
}
}
outCh <- data
}
close(outCh)
}
4. 发布插件 (workflows/publisher.go
)
package workflow
import "fmt"
// 数据发布插件
func PublishData(inCh <-chan Data) {
var successCount int
for data := range inCh {
// 模拟发布到数据库/消息队列
fmt.Printf("[发布] ID:%s 最终值:%.2f 文本:%s\n",
data.ID, data.Value, data.Text)
successCount++
}
fmt.Printf("发布完成,成功%d条\n", successCount)
}
5. 主程序 (main.go
)
通过通道阻塞和goroutine协作,确保数据按"采集→处理→发布"顺序流动,主goroutine在PublishData处阻塞等待流程完成
package main
import (
"workflow/workflows"
)
func main() {
// 初始化带缓冲的通道
collectCh := make(chan workflow.Data, 3)
processCh := make(chan workflow.Data, 3)
// 启动工作流
go workflows.CollectData(collectCh) // 采集数据
go workflows.ProcessData(collectCh, processCh) // 处理数据
workflows.PublishData(processCh) // 发布数据
}
三、执行流程说明
1. 流程图 (Mermaid)
2. 控制台输出示例
[采集] ID:D001 原始值:10.5
[采集] ID:D002 原始值:25.0
[采集] ID:D003 原始值:99.9
[处理] 收到数据:D001 类型:clean
[处理] 收到数据:D002 类型:transform
[发布] ID:D001 最终值:10.50 文本:Order #123
[处理] 收到数据:D003 类型:analyze
[发布] ID:D002 最终值:37.50 文本:VIP Customer
[发布] ID:D003 最终值:99.90 文本:[高价值]Urgent!
发布完成,成功3条
插件可插拔工作流
为了提高工作流的可扩展性,下面演示如何实现一个可插拔的工作流,实现思路。
可插拔工作流的核心实现原理:通过插件注册中心动态管理插件元信息,工作流引擎在执行时根据注册信息动态调用插件服务,每个插件通过标准化的输入输出契约进行数据交互,从而实现无需修改核心代码即可自由插拔各类功能模块。
系统架构图
项目结构
workflow-engine/
├── main.go # 引擎主入口
├── engine/
│ ├── workflow_builder.go # 工作流创建和管理
│ ├── registry.go # 插件注册中心
│ ├── executor.go # 工作流执行器
│ ├── scheduler.go # 任务调度器
│ └── types.go # 类型定义
└── config/
└── workflows/ # 工作流定义
└── demo.yaml
plugin-services/ # 独立插件服务(不同进程)
├── collector/
│ ├── main.go # 数据采集服务
│ └── Dockerfile
├── processor1/
│ ├── main.go # 数据处理服务1
│ └── Dockerfile
├── processor2/
│ ├── main.go # 数据处理服务2
│ └── Dockerfile
└── publisher/
├── main.go # 数据发布服务
└── Dockerfile
核心代码实现
1. 类型定义 (engine/types.go
)
package engine
// PluginMeta 定义插件的元数据信息,用于注册和描述插件能力
type PluginMeta struct {
// Name 插件的唯一标识名称(建议使用小写+下划线命名)
// 示例: "temperature_collector"
Name string `json:"name"`
// Endpoint 插件服务的HTTP访问地址(必须包含完整路径)
// 格式: "http://<host>:<port>/<path>"
// 示例: "http://192.168.1.10:8080/data_collect"
Endpoint string `json:"endpoint"`
// Type 插件类型分类(必须为以下四种之一)
// "collector" - 数据采集型(工作流起点,必须实现流式接口)
// "stream_processor" - 流式处理型(逐条数据处理)
// "batch_processor" - 批处理型(批量数据处理)
// "publisher" - 数据输出型(工作流终点,建议实现批处理接口)
Type string `json:"type"`
// InputSchema 定义插件输入数据的结构契约
// key: 参数名称, value: 数据类型("int"/"float"/"string"/"bool")
// 示例: {"sensor_id":"string", "value":"float"}
InputSchema map[string]string `json:"inputSchema"`
// OutputSchema 定义插件输出数据的结构契约
// 格式要求与InputSchema相同
// 示例: {"status":"string", "processed_value":"float"}
OutputSchema map[string]string `json:"outputSchema"`
}
// Workflow 定义完整的工作流执行流程
type Workflow struct {
// Name 工作流的业务标识名称
// 示例: "temperature_processing"
Name string `json:"name"`
// Steps 按执行顺序排列的插件名称数组
// 编排规则:
// 1. 首元素必须是"collector"类型插件
// 2. 中间可混合"stream_processor"和"batch_processor"
// 3. 末元素必须是"publisher"类型插件
// 示例: ["temp_collector", "filter_processor", "avg_calculator", "db_publisher"]
Steps []string `json:"steps"`
}
// PluginResponse 定义插件执行的标准响应格式
type PluginResponse struct {
// Data 插件处理后的业务数据
// 要求:必须符合插件元数据中OutputSchema的定义
// 示例: {"avg_temp": 25.6, "samples": 100}
Data interface{} `json:"data"`
// Error 错误信息(成功执行时必须为空)
// 格式要求:
// 1. 非空字符串表示执行失败
// 2. 建议格式:"ERROR_TYPE: error description"
// 示例: "INVALID_INPUT: temperature value out of range"
Error string `json:"error,omitempty"` // omitempty表示空值时不输出
const (
PluginTypeCollector = "collector" // 数据采集型插件
PluginTypeStreamProcessor = "stream_processor" // 流式处理型插件
PluginTypeBatchProcessor = "batch_processor" // 批处理型插件
PluginTypePublisher = "publisher" // 数据发布型插件
)
}
2. 插件注册中心 (engine/registry.go
)
package engine
import (
"sync"
)
// Registry 插件注册中心,负责插件的生命周期管理和服务发现
// 采用线程安全设计,支持并发注册和查询
type Registry struct {
// plugins 存储所有已注册插件的元信息
// key: 插件名称(需保证唯一性)
// value: 插件元数据(PluginMeta)
plugins map[string]PluginMeta
// mu 读写锁,保证并发安全
// 规则:
// - 写操作(注册/注销)使用写锁(Lock/Unlock)
// - 读操作(查询)使用读锁(RLock/RUnlock)
mu sync.RWMutex
}
// NewRegistry 创建并初始化一个新的插件注册中心实例
// 返回:
// *Registry: 初始化后的注册中心指针,插件列表为空
func NewRegistry() *Registry {
return &Registry{
plugins: make(map[string]PluginMeta), // 初始化空的插件映射
}
}
// Register 注册一个新插件(线程安全)
// 参数:
// meta: PluginMeta 包含插件元信息
// 返回值:
// error: 注册成功返回nil,插件已存在返回ErrPluginExists
// 注意:
// - 会覆盖同名的旧插件(根据业务需求可调整)
// - 调用方应确保meta.Endpoint可访问
func (r *Registry) Register(meta PluginMeta) error {
r.mu.Lock() // 获取写锁
defer r.mu.Unlock() // 确保锁释放
// 检查插件是否已存在
if _, exists := r.plugins[meta.Name]; exists {
return ErrPluginExists // 返回预定义的错误
}
// 注册插件到内存映射
r.plugins[meta.Name] = meta
return nil
}
// GetPlugin 根据插件名称查询插件信息(线程安全)
// 参数:
// name: string 插件名称
// 返回值:
// PluginMeta: 插件元数据(未找到时返回空结构体)
// bool: 是否存在(true表示存在)
func (r *Registry) GetPlugin(name string) (PluginMeta, bool) {
r.mu.RLock() // 获取读锁
defer r.mu.RUnlock() // 确保锁释放
meta, exists := r.plugins[name]
return meta, exists
}
// GetPluginsByType 根据插件类型筛选插件(线程安全)
// 参数:
// pluginType: string 插件类型
// 可选值: "collector"/"stream_processor"/"batch_processor"/"publisher"
// 返回值:
// []PluginMeta: 匹配类型的插件数组(可能为空数组)
// 性能提示:
// - 频繁调用时可考虑增加缓存优化
func (r *Registry) GetPluginsByType(pluginType string) []PluginMeta {
r.mu.RLock() // 获取读锁
defer r.mu.RUnlock() // 确保锁释放
var result []PluginMeta
// 遍历所有插件进行筛选
for _, meta := range r.plugins {
if meta.Type == pluginType {
result = append(result, meta)
}
}
return result
}
3. 工作流执行器 (engine/executor.go
)
package engine
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"reflect"
"time"
)
// 执行模式常量
const (
ExecutionModeStream = "stream" // 流式处理(逐条)
ExecutionModeBatch = "batch" // 批处理(批量)
)
// ExecutePlugin 执行插件(自动选择模式)
func ExecutePlugin(meta PluginMeta, input interface{}) (interface{}, error) {
// 根据插件类型动态选择执行模式
switch meta.Type {
case PluginTypeBatchProcessor:
return executeBatchMode(meta, input) // 批处理模式
default:
return executeStreamMode(meta, input) // 默认流式模式(含采集器/发布器)
}
}
// 流式处理(实时逐条)
func executeStreamMode(meta PluginMeta, input interface{}) (interface{}, error) {
reqBody, _ := json.Marshal(map[string]interface{}{"data": input})
client := &http.Client{Timeout: 5 * time.Second} // 短超时确保实时性
resp, err := client.Post(meta.Endpoint, "application/json", bytes.NewReader(reqBody))
if err != nil {
return nil, &PluginError{
PluginName: meta.Name,
Message: "流式请求失败: " + err.Error(),
Mode: ExecutionModeStream,
}
}
defer resp.Body.Close()
var result PluginResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, &PluginError{
PluginName: meta.Name,
Message: "流式响应解析失败: " + err.Error(),
Mode: ExecutionModeStream,
}
}
if result.Error != "" {
return nil, &PluginError{
PluginName: meta.Name,
Message: "流式处理错误: " + result.Error,
Mode: ExecutionModeStream,
}
}
return result.Data, nil
}
// 批处理(批量高效)
func executeBatchMode(meta PluginMeta, input interface{}) (interface{}, error) {
batchInput := map[string]interface{}{"batch": toBatchSlice(input)}
reqBody, _ := json.Marshal(batchInput)
client := &http.Client{Timeout: 30 * time.Second} // 长超时适应大数据量
resp, err := client.Post(meta.Endpoint, "application/batch", bytes.NewReader(reqBody))
if err != nil {
return nil, &PluginError{
PluginName: meta.Name,
Message: "批处理请求失败: " + err.Error(),
Mode: ExecutionModeBatch,
}
}
defer resp.Body.Close()
var result struct {
Batch []interface{} `json:"batch"` // 批处理专用响应格式
Error string `json:"error"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, &PluginError{
PluginName: meta.Name,
Message: "批处理响应解析失败: " + err.Error(),
Mode: ExecutionModeBatch,
}
}
if result.Error != "" {
return nil, &PluginError{
PluginName: meta.Name,
Message: "批处理执行错误: " + result.Error,
Mode: ExecutionModeBatch,
}
}
return result.Batch, nil
}
// 数据标准化转换(兼容单条/数组)
func toBatchSlice(input interface{}) []interface{} {
if arr, ok := input.([]interface{}); ok {
return arr
}
val := reflect.ValueOf(input)
if val.Kind() == reflect.Slice {
result := make([]interface{}, val.Len())
for i := 0; i < val.Len(); i++ {
result[i] = val.Index(i).Interface()
}
return result
}
return []interface{}{input} // 单条数据自动包装
}
4. 工作流构建器 (workflow-engine
)
package engine
import (
"errors"
)
// WorkflowBuilder 工作流构建器(线程安全)
type WorkflowBuilder struct {
reg *Registry
mu sync.RWMutex
}
// NewWorkflowBuilder 创建构建器实例
func NewWorkflowBuilder(reg *Registry) *WorkflowBuilder {
return &WorkflowBuilder{
reg: reg,
}
}
// CreateWorkflow 创建新工作流(基础版)
// 参数:
// name: 工作流名称
// steps: 插件执行顺序
// 返回值:
// *Workflow: 创建的工作流实例
// error: 当插件不存在或流程不合法时返回错误
func (wb *WorkflowBuilder) CreateWorkflow(name string, steps []string) (*Workflow, error) {
wb.mu.Lock()
defer wb.mu.Unlock()
// 校验工作流基础规则
if len(steps) == 0 {
return nil, errors.New("工作流必须包含至少一个步骤")
}
// 校验首节点是否为collector类型
if first, exists := wb.reg.GetPlugin(steps[0]); !exists || first.Type != PluginTypeCollector {
return nil, errors.New("工作流必须以collector插件开始")
}
// 校验末节点是否为publisher类型
if last, exists := wb.reg.GetPlugin(steps[len(steps)-1]); !exists || last.Type != PluginTypePublisher {
return nil, errors.New("工作流必须以publisher插件结束")
}
// 校验中间节点类型
for i := 1; i < len(steps)-1; i++ {
meta, exists := wb.reg.GetPlugin(steps[i])
if !exists {
return nil, fmt.Errorf("插件未注册: %s", steps[i])
}
if meta.Type != PluginTypeStreamProcessor && meta.Type != PluginTypeBatchProcessor {
return nil, fmt.Errorf("中间节点必须是processor类型: %s", steps[i])
}
}
return &Workflow{
Name: name,
Steps: steps,
}, nil
}
// CreateWorkflowFromConfig 从配置文件创建工作流
func (wb *WorkflowBuilder) CreateWorkflowFromConfig(configPath string) (*Workflow, error) {
data, err := os.ReadFile(configPath)
if err != nil {
return nil, err
}
var config struct {
Name string `json:"name"`
Steps []string `json:"steps"`
}
if err := json.Unmarshal(data, &config); err != nil {
return nil, err
}
return wb.CreateWorkflow(config.Name, config.Steps)
}
// ValidateWorkflow 校验工作流合法性
func (wb *WorkflowBuilder) ValidateWorkflow(flow *Workflow) error {
_, err := wb.CreateWorkflow(flow.Name,package main
import (
"workflow-engine/engine"
"encoding/json"
"fmt"
"net/http"
"log"
"github.com/gorilla/mux"
)
func main() {
// 初始化注册中心
reg := engine.NewRegistry()
// 预注插件集(每个类型各一个)
preRegisterPlugins(reg)
// 创建路由
r := mux.NewRouter()
// 插件注册接口
r.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) {
var meta engine.PluginMeta
if err := json.NewDecoder(r.Body).Decode(&meta); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := reg.Register(meta); err != nil {
http.Error(w, err.Error(), http.StatusConflict)
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{"status": "registered"})
}).Methods("POST")
// 工作流执行接口
r.HandleFunc("/execute/{workflow}", func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
workflowName := vars["workflow"]
// 根据工作流名称选择预定义工作流
flow, err := getPredefinedWorkflow(workflowName)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
// 执行工作流
result, err := engine.ExecuteWorkflow(flow, reg, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(map[string]interface{}{
"workflow": workflowName,
"status": "completed",
"result": result,
})
}).Methods("POST")
// 启动服务
log.Println("Workflow engine started on :8080")
log.Fatal(http.ListenAndServe(":8080", r))
}
//插件注册函数
func preRegisterPlugins(reg *engine.Registry) {
// 1. 数据采集器
reg.Register(engine.PluginMeta{
Name: "sensor_collector",
Endpoint: "http://sensor-collector:8080/collect",
Type: engine.PluginTypeCollector,
InputSchema: map[string]string{
"sensor_id": "string", // 传感器ID
"interval": "int", // 采集间隔(ms)
},
OutputSchema: map[string]string{
"sensor_id": "string", // 传感器ID
"value": "float", // 采集值
"unit": "string", // 单位
"timestamp": "string", // 时间戳
},
})
// 2. 流式处理器
reg.Register(engine.PluginMeta{
Name: "data_filter",
Endpoint: "http://data-filter:8081/process",
Type: engine.PluginTypeStreamProcessor,
InputSchema: map[string]string{
"value": "float", // 输入值
"min": "float", // 最小值
"max": "float", // 最大值
},
OutputSchema: map[string]string{
"value": "float", // 输出值
"is_filtered": "bool", // 是否被过滤
},
})
// 3. 批量处理器
reg.Register(engine.PluginMeta{
Name: "stats_aggregator",
Endpoint: "http://stats-aggregator:8082/aggregate",
Type: engine.PluginTypeBatchProcessor,
InputSchema: map[string]string{
"data": "[]float64", // 数据集
"period": "string", // 统计周期 (hour/day)
},
OutputSchema: map[string]string{
"min": "float", // 最小值
"max": "float", // 最大值
"average": "float", // 平均值
"count": "int", // 数据点数
},
})
// 4. 数据发布器
reg.Register(engine.PluginMeta{
Name: "db_writer",
Endpoint: "http://db-writer:8083/write",
Type: engine.PluginTypePublisher,
InputSchema: map[string]string{
"table": "string", // 表名
"data": "object", // 数据对象
},
OutputSchema: map[string]string{
"rows_affected": "int", // 受影响行数
"status": "string", // 成功/失败
},
})
}
// 工作流存储
var predefinedWorkflows = map[string]engine.Workflow{
// 实时流式处理工作流
"realtime_processing": {
Name: "realtime_processing",
Steps: []string{
"sensor_collector", // 数据采集
"data_filter", // 流式处理
"db_writer", // 数据存储
},
},
// 批量分析工作流
"batch_analysis": {
Name: "batch_analysis",
Steps: []string{
"sensor_collector", // 数据采集
"stats_aggregator", // 批量处理
"db_writer", // 数据存储
},
},
}
// 获取预定义工作流
func getPredefinedWorkflow(name string) (engine.Workflow, error) {
if flow, exists := predefinedWorkflows[name]; exists {
return flow, nil
}
return engine.Workflow{}, fmt.Errorf("workflow %s not found", name)
} flow.Steps)
return err
}
5. 主程序 (main.go
)
package main
import (
"workflow-engine/engine"
"encoding/json"
"fmt"
"net/http"
"log"
"github.com/gorilla/mux"
)
func main() {
// 初始化注册中心
reg := engine.NewRegistry()
// 预注册插件
preRegisterPlugins(reg)
// 创建路由
r := mux.NewRouter()
// 插件注册接口
r.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) {
var meta engine.PluginMeta
if err := json.NewDecoder(r.Body).Decode(&meta); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := reg.Register(meta); err != nil {
http.Error(w, err.Error(), http.StatusConflict)
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{"status": "registered"})
}).Methods("POST")
// 工作流执行接口
r.HandleFunc("/execute/{workflow}", func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
workflowName := vars["workflow"]
// 根据工作流名称选择预定义工作流
flow, err := getPredefinedWorkflow(workflowName)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
// 执行工作流
result, err := engine.ExecuteWorkflow(flow, reg, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(map[string]interface{}{
"workflow": workflowName,
"status": "completed",
"result": result,
})
}).Methods("POST")
// 启动服务
log.Println("Workflow engine started on :8080")
log.Fatal(http.ListenAndServe(":8080", r))
}
// 插件注册函数
func preRegisterPlugins(reg *engine.Registry) {
// 1. 数据采集器
reg.Register(engine.PluginMeta{
Name: "sensor_collector",
Endpoint: "http://sensor-collector:8080/collect",
Type: engine.PluginTypeCollector,
InputSchema: map[string]string{
"sensor_id": "string", // 传感器ID
"interval": "int", // 采集间隔(ms)
},
OutputSchema: map[string]string{
"sensor_id": "string", // 传感器ID
"value": "float", // 采集值
"unit": "string", // 单位
"timestamp": "string", // 时间戳
},
})
// 2. 流式处理器
reg.Register(engine.PluginMeta{
Name: "data_filter",
Endpoint: "http://data-filter:8081/process",
Type: engine.PluginTypeStreamProcessor,
InputSchema: map[string]string{
"value": "float", // 输入值
"min": "float", // 最小值
"max": "float", // 最大值
},
OutputSchema: map[string]string{
"value": "float", // 输出值
"is_filtered": "bool", // 是否被过滤
},
})
// 3. 批量处理器
reg.Register(engine.PluginMeta{
Name: "stats_aggregator",
Endpoint: "http://stats-aggregator:8082/aggregate",
Type: engine.PluginTypeBatchProcessor,
InputSchema: map[string]string{
"data": "[]float64", // 数据集
"period": "string", // 统计周期 (hour/day)
},
OutputSchema: map[string]string{
"min": "float", // 最小值
"max": "float", // 最大值
"average": "float", // 平均值
"count": "int", // 数据点数
},
})
// 4. 数据发布器
reg.Register(engine.PluginMeta{
Name: "db_writer",
Endpoint: "http://db-writer:8083/write",
Type: engine.PluginTypePublisher,
InputSchema: map[string]string{
"table": "string", // 表名
"data": "object", // 数据对象
},
OutputSchema: map[string]string{
"rows_affected": "int", // 受影响行数
"status": "string", // 成功/失败
},
})
}
// 工作流存储
var predefinedWorkflows = map[string]engine.Workflow{
// 实时流式处理工作流
"realtime_processing": {
Name: "realtime_processing",
Steps: []string{
"sensor_collector", // 数据采集
"data_filter", // 流式处理
"db_writer", // 数据存储
},
},
// 批量分析工作流
"batch_analysis": {
Name: "batch_analysis",
Steps: []string{
"sensor_collector", // 数据采集
"stats_aggregator", // 批量处理
"db_writer", // 数据存储
},
},
}
// 获取预定义工作流
func getPredefinedWorkflow(name string) (engine.Workflow, error) {
if flow, exists := predefinedWorkflows[name]; exists {
return flow, nil
}
return engine.Workflow{}, fmt.Errorf("workflow %s not found", name)
}