0.Elsa源码探索-入门简介

一、Elsa 是什么

Elsa 是一个开源的 .NET 工作流引擎,让你能在任意 .NET 应用中定义和执行工作流。

很多系统上线后,真正拖慢团队的不是不会开发新功能,而是历史代码改不动。业务规则一变就要发版、跨部门协作缺少统一对接规范、问题发生后很难追踪定位。

工作流的意义在于,把这些经常变化的流程抽出来,变成可配置、可观测、可复用的资产。有了工作流,代码执行过程不再是“藏在代码里的隐形逻辑”,而是变成可组合、可观测的显式系统。

除了上述的业务价值外,工作流对框架能力的复用与拓展也非常有帮助,当发送各类消息、调用外部API、处理文件转换这些底层能力被当成工具摆在眼前时,业务开发者就能更好的专注于业务逻辑本身,而不至于到处造轮子。

为什么选 Elsa?首先肯定是因为它由 dotnte 语言编写,同时它功能强大、社区活跃、代码质量优良...在设计思路和完整度上更是遥遥领先!

300*400

当然 Elsa 也有一定的局限性,比如:

  • 前端使用的 Blazor 技术比较小众、页面风格也不太符合中国人的胃口。
  • 对标 n8n、dify、扣子等 AI 工作流时稍显功能不足。

好在这些缺点并不难解决,我们会在后续分享中逐步说明具体解决方案。

二、Elsa 整体架构

  • elsa-core:后端引擎,提供 REST API 和工作流执行能力。 传送地址
  • elsa-studio:前端管理界面,可视化设计和监控。 传送地址

image

三、快速体验

3.1.1 Docker(版本老旧,不推荐)

docker pull elsaworkflows/elsa-server-and-studio-v3:latest

docker run -t -i \
  -e ASPNETCORE_ENVIRONMENT=Development \
  -e HTTP_PORTS=8080 \
  -e HTTP__BASEURL=http://localhost:13000 \
  -p 7113:8080 \
  elsaworkflows/elsa-server-and-studio-v3:latest

3.1.2 本地运行源代码(版本较新、可读源码,推荐)

  • 第一步:拉代码

    git clone https://github.com/elsa-workflows/elsa-core.git
    git clone https://github.com/elsa-workflows/elsa-studio.git
    
  • 第二步:启动后端,打开 elsa-core\Elsa.sln,找到并运行启动项目 src/apps/Elsa.Server.Web,启动后默认监听 https://localhost:5001。数据库使用的是 SQLite 且会自动项目根目录完成初始化,无需任何配置。

  • 第三步:启动前端,打开 elsa-studio\Elsa.Studio.sln,找到并运行:

    src/hosts/Elsa.Studio.Host.Wasm  (WASM 模式,在浏览器跑)
    或
    src/hosts/Elsa.Studio.Host.Server  (Server 模式,在服务端渲染)
    

3.1.3 登录

打开浏览器访问 https://localhost:7113 , 用户名: admin,密码: password


3.1.4 创建第一个工作流

登录后,可按以下步骤体验一个最简单的工作流:

  1. 点击左侧菜单 Workflow Definitions 点击 Create workflow,输入工作流名称
    image

  2. 在画布上拖入 HTTP Endpoint 活动,配置路径为 /helloworld,请求方式为 GET`
    image

  3. 再拖入 HTTP Response 活动, Content 内容写 helloworld
    image

  4. 点击运行按钮后会自动跳转到工作流实例页面,且状态是挂起
    image

  5. 用浏览器或 curl 访问 https://localhost:5001/workflows/helloworld 页面上会得到helloworld 的响应,并且后台会刷新出执行过程日志。
    image

四、Elsa 设计上的精良之处

4.1 Feature 模块化配置系统

Elsa 把每个功能单元封装为 IFeature,通过 IModule 统一管理依赖和安装,每个 Feature 通过 [DependsOn] 声明依赖,Module.Apply() 自动做拓扑排序,确保按正确顺序安装。不安装的模块不注册,没有任何开销

services.AddElsa(module => { module.UseWorkflows() // 核心执行引擎 .UseWorkflowManagement(m => m.UseEntityFrameworkCore(ef => ef.UseSqlite())) .UseWorkflowRuntime(r => r.UseEntityFrameworkCore()) .UseDistributedRuntime() // 一行切换分布式模式 .UseJavaScript() // 按需添加 JS 表达式支持 .UseLiquid() // 按需添加 Liquid 模板支持 .UseHttp(); // 按需添加 HTTP 触发器支持 .UseDistributedRuntime() // 一行切换分布式模式 .UseJavaScript() // 按需添加 JS 表达式支持 .UseLiquid() // 按需添加 Liquid 模板支持 .UseHttp(); // 按需添加 HTTP 触发器支持 });


4.2 丰富的预留拓展点

Elsa 的执行过程并不是一个又臭又长的大函数,而是两条可扩展中间件管道

IWorkflowExecutionPipeline(工作流级) └── DefaultActivitySchedulerMiddleware(调度循环) └── IActivityExecutionPipeline(活动级) └── DefaultActivityInvokerMiddleware(活动执行) └── DefaultActivitySchedulerMiddleware(调度循环) └── IActivityExecutionPipeline(活动级) └── DefaultActivityInvokerMiddleware(活动执行)

类比 ASP.NET Core 的 app.UseXxx() 管道,你可以在任意位置插入自己的中间件:

// 在所有活动执行前后记录耗时 public class TimingMiddleware : IActivityExecutionMiddleware { public async ValueTask InvokeAsync(ActivityExecutionContext ctx, ActivityMiddlewareDelegate next) { var sw = Stopwatch.StartNew(); await next(ctx); Console.WriteLine($"{ctx.Activity.Type} 耗时: {sw.ElapsedMilliseconds}ms"); } }

不需要改任何引擎代码,直接注入中间件就能扩展执行行为。

4.3 灵活的取值逻辑

这是整个项目中最值得学习的设计,活动中所有输入和输出都可以是动态表达式,而不是硬编码值。这意味着在编排的过程中我仅关注静态的逻辑即可,框架会将运行时的实际值放到指定的位置上。

以上文中 demo 中的场景为例,在设置 Http 响应的时候,我们使用的是固定值,而真实场景中一般不会这么简单,此时我们可以有更多选择:

  1. 使用全局变量

    image

  2. 使用函数

    image

    • 代码说明:

      const variable = getVariable('Variable2'); // 使用 Elsa 内置函数获取全局变量
      var today = new Date();// 使用原生的 js 对象
      
      // 完全支持 js 习惯的写法
      var DD = String(today.getDate()).padStart(2, '0');
      var MM = String(today.getMonth() + 1).padStart(2, '0');
      var yyyy = today.getFullYear();
      
      hh =  String(today.getHours()).padStart(2, '0');
      mm = String(today.getMinutes()).padStart(2, '0');
      ss = String(today.getSeconds()).padStart(2, '0');
      today = yyyy + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss;
      
      return variable + today;
      

      除了 getVariable Elsa 内置还了很多系统函数,可以轻松获取到任何你想获取的值,具体用法将会在后续文章中进行分享。

4.4 工作流即代码(Code-first)

Elsa 不强迫你用可视化设计器——你可以完全用 C# 定义工作流:

    public class OrderApprovalWorkflow : WorkflowBase
    {
        protected override void Build(IWorkflowBuilder builder)
        {
            builder.Root = new Sequence
            {
                Activities =
                {
                    // 等待 HTTP 请求触发(外部 POST 请求进来时启动)
                    new HttpEndpoint
                    {
                        Path = new("/orders"),
                        SupportedMethods = new(new[] { HttpMethods.Post }),
                        CanStartWorkflow = true
                    },
                    // 发邮件通知审批人
                    new SendEmail
                    {
                        To = new(new[] { "manager@company.com" }),
                        Subject = new("新订单待审批"),
                        Body = new("{{ Input.orderDetails }}")
                    },
                    // 等待审批结果(挂起,等下一个 HTTP 请求)
                    new HttpEndpoint
                    {
                        Path = new("/orders/approve"),
                        SupportedMethods = new(new[] { HttpMethods.Post })
                    },
                    // 根据审批结果分支
                    new If
                    {
                        Condition = new("{{ Input.approved }}"),
                        Then = new Sequence { Activities = { new SendEmail { /* 通知通过 */ } } },
                        Else = new Sequence { Activities = { new SendEmail { /* 通知拒绝 */ } } }
                    }
                }
            };
        }
    }

4.5 支持多种持久化存储方案

Elsa 的 IWorkflowDefinitionStoreIWorkflowInstanceStore 等都是接口,引擎本身不依赖任何具体数据库,甚至可以实现自己的 Store 接口,对接任意存储...

// 用 SQLite
management.UseEntityFrameworkCore(ef => ef.UseSqlite());

// 换成 PostgreSQL
management.UseEntityFrameworkCore(ef => ef.UsePostgreSql(connectionString));

// 换成 SQL Server
management.UseEntityFrameworkCore(ef => ef.UseSqlServer(connectionString));

4.6 一行代码启动分布式水平扩展

单节点模式下直接本地执行;需要多节点时:

module.UseDistributedRuntime();// 只需加这一行,本地运行时自动升级为分布式运行时

通过本篇内容您仅需对 Elsa 这个 dotnet 工作流框架建立一个初印象即可,我们会从源码角度逐步展开更多原理和使用细节。

posted @ 2026-04-11 20:44  叨奈特挖井人  阅读(61)  评论(0)    收藏  举报