一個適合於.NET Core的超輕量級工作流引擎:Workflow-Core


一、關於Workflow-Core

  近期工作上有一個工作流的開發需求,自己基於面向對象和職責鏈模式搗鼓了一套小框架,后來在github上發現一個輕量級的工作流引擎輪子:Workflow-Core,看完其wiki之后決定放棄之前自己造的輪子,使用這個開源項目來改造,也就有了這一篇博文。

  

  Workflow-Core是一個基於.NET Standard的輕量級工作流引擎,其GitHub地址為:https://github.com/danielgerlag/workflow-core,目前有超過1200+個star。它提供了FluentAPI、多任務、持久化以及並行處理的功能,適合於小型工作流、責任鏈的需求開發。

  由於Workflow-Core支持工作流長期運行,因此Workflow-Core支持以下多種數據源格式的持久化,可以通過安裝不同的Provider包來實現對應的持久化:

  • (默認提供,用於測試和開發)內存
  • MongoDB
  • MS SQL Server
  • MySql
  • Sqlite
  • Redis
  • PostgreSQL

  立刻上手把,Nuget上安裝一把,目前最新版本2.0.0:

PM> Install-Package WorkflowCore

二、Workflow-Core的基本使用

2.1 Hello World

  這里創建了一個.NET Core控制台應用程序,快速演示第一個Workflow-Core的Hello World,展示如何開始一個Workflow:

  (1)定義一個實現IWorkflow接口的Workflow:

    public class HelloWorldWorkflow : IWorkflow
    {
        public string Id => "HelloWorld";

        public int Version => 1;

        public void Build(IWorkflowBuilder<object> builder)
        {
            builder
                .StartWith<HelloWorld>()
                .Then<ActiveWorld>()
                .Then<GoodbyeWorld>();
        }
    }

  這里定義了一個HelloWorldWorkflow,其版本號為1,它有3個步驟:HelloWorld、ActiveWorld和GoodbyeWorld,會依次執行。

  (2)定義三個繼承自StepBody類的步驟類:

    public class HelloWorld : StepBody
    {
        public override ExecutionResult Run(IStepExecutionContext context)
        {
            Console.WriteLine("Hello World!");
            return ExecutionResult.Next();
        }
    }

    public class ActiveWorld : StepBody
    {
        public override ExecutionResult Run(IStepExecutionContext context)
        {
            Console.WriteLine("I am activing in the World!");
            return ExecutionResult.Next();
        }
    }

    public class GoodbyeWorld : StepBody
    {
        public override ExecutionResult Run(IStepExecutionContext context)
        {
            Console.WriteLine("Goodbye World!");
            return ExecutionResult.Next();
        }
    }

  (3)ServiceCollection中注入Workflow-Core相關組件

    private static IServiceProvider ConfigureServices()
    {
        IServiceCollection services = new ServiceCollection();
        services.AddLogging(); // WorkflowCore需要用到logging service
        services.AddWorkflow();

        var serviceProvider = services.BuildServiceProvider();

        return serviceProvider;
    }

  (4)在Program.cs的Main方法中獲取到注入的host並執行工作流

        public static void Main(string[] args)
        {
            var serviceProvider = ConfigureServices();
            var host = serviceProvider.GetService<IWorkflowHost>();
            host.RegisterWorkflow<HelloWorldWorkflow>();
            host.Start();

            // Demo1:Hello World
            host.StartWorkflow("HelloWorld");

            Console.ReadKey();
            host.Stop();
        }    

  這里傳入的是Workflow的Id,Workflow-Core會根據Id去自動匹配最新版本的對應Workflow,運行結果如下:

  

2.2 If語句

  在工作流處理中,往往會有很多的條件判斷,那么在Workflow-Core中也提供了直接的If功能,如下面這個IfStatementWorkflow所示:

    public class IfStatementWorkflow : IWorkflow<MyData>
    {
        public string Id => "if-sample";

        public int Version => 1;

        public void Build(IWorkflowBuilder<MyData> builder)
        {
            builder
                .StartWith<SayHello>()
                .If(data => data.Counter < 3).Do(then => then
                        .StartWith<PrintMessage>()
                            .Input(step => step.Message, data => "Outcome is less than 3")
                )
               .If(data => data.Counter < 5).Do(then => then
                        .StartWith<PrintMessage>()
                            .Input(step => step.Message, data => "Outcome is less than 5")
                )
                .Then<SayGoodbye>();
        }
    }

  這個傳遞進來的MyData的定義如下:

    public class MyData
    {
        public int Counter { get; set; }
    }

  當傳遞進來的MyData的Counter屬性<3 或 <5時會有不同的分支進行邏輯的處理。

2.3 MySQL持久化支持

  想要將工作流配置持久化到MySQL,只需以下兩步:

  (1)通過Nuget安裝MySQL Provider包:

PM> Install-Package WorkflowCore.Persistence.MySQL

  (2)注入到ServiceCollection

services.AddWorkflow(x => x.UseMySQL(@"Server=127.0.0.1;Database=workflow;User=root;Password=password;", true, true));

  一旦啟動,你就會發現Workflow-Core自動幫你創建了很多表用於持久化工作流配置和實例。

  

2.4 計划任務和循環任務

  Workflow-Core還集成了計划任務和循環任務的功能:

  (1)計划任務:比如在工作流步驟中設置一個延遲5分鍾執行的計划任務

builder
    .StartWith(context => Console.WriteLine("Hello"))
    .Schedule(data => TimeSpan.FromSeconds(5)).Do(schedule => schedule
        .StartWith(context => Console.WriteLine("Doing scheduled tasks"))
    )
    .Then(context => Console.WriteLine("Doing normal tasks"));

  (2)循環任務:比如在工作流步驟中設置一個延遲5分鍾進行的循環任務,知道Counter > 5才結束

builder
    .StartWith(context => Console.WriteLine("Hello"))
    .Recur(data => TimeSpan.FromSeconds(5), data => data.Counter > 5).Do(recur => recur
        .StartWith(context => Console.WriteLine("Doing recurring task"))
    )
    .Then(context => Console.WriteLine("Carry on"));

2.5 Saga支持

  了解分布式事務方案的童鞋應該都知道Saga,在Workflow-Core中也有支持,這是一個十分有用的功能:

  (1)比如:在創建一個客戶信息之后,將其推送到Salesforce和ERP,如果推送過程中發生了錯誤,那么就通過重試進行補償,並且重試有時間間隔。

      builder
            .StartWith<CreateCustomer>()
            .Then<PushToSalesforce>()
                .OnError(WorkflowErrorHandling.Retry, TimeSpan.FromMinutes(10))
            .Then<PushToERP>()
                .OnError(WorkflowErrorHandling.Retry, TimeSpan.FromMinutes(10));

  (2)又比如:當Task2發生異常時,Workflow-Core會幫助執行UndoTask2 和 UndoTask1 幫你回滾數據以恢復狀態。

builder
    .StartWith<LogStart>()
    .Saga(saga => saga
        .StartWith<Task1>()
            .CompensateWith<UndoTask1>()
        .Then<Task2>()
            .CompensateWith<UndoTask2>()
        .Then<Task3>()
            .CompensateWith<UndoTask3>()
    )
    .OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromMinutes(10))
    .Then<LogEnd>();

  更多Saga示例,請參考:https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample17

三、在ASP.NET Core中使用Workflow-Core

3.1 注入與初始化

  (1)注入:使用AddWorkflow()擴展方法

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddWorkflow();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

  (2)初始化:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
            .......      
            app.UseWorkflow();
    }

  擴展方法如下:

    public static class ConfigureExtensions
    {
        public static IApplicationBuilder UseWorkflow(this IApplicationBuilder app)
        {
            var host = app.ApplicationServices.GetService<IWorkflowHost>();
            host.RegisterWorkflow<EdcWorkflow>();
            host.RegisterWorkflow<EdcDataWorkflow, EdcData>();
            host.Start();

            var appLifetime = app.ApplicationServices.GetService<IApplicationLifetime>();
            appLifetime.ApplicationStopping.Register(() =>
            {
                host.Stop();
            });

            return app;
        }
    }

  這里需要注意的就是:將你要用到的所有Workflow都事先進行Register注冊。

3.2 通過DI獲取使用  

  在你想要用到的地方,無論是Controller還是Service,通過依賴注入獲取到Host,並使用它:

    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        private IWorkflowController _workflowService;

        public ValuesController(IWorkflowController workflowService)
        {
            _workflowService = workflowService;
        }

        // GET api/values
        [HttpGet]
        public async Task<IEnumerable<string>> Get()
        {
            await _workflowService.StartWorkflow("EdcWorkflow");
            return new string[] { "EdcWorkflow v1" };
        }

        // GET api/values/5
        [HttpGet("{id}")]
        public async Task<string> Get(int id)
        {
            await _workflowService.StartWorkflow("EdcDataWorkflow", new EdcData() { Id = id });
            return "EdcDataWorkflow v1";
        }
    }

  這兩個Workflow的定義如下:

    public class EdcWorkflow : IWorkflow
    {
        public string Id => "EdcWorkflow";

        public int Version => 1;

        public void Build(IWorkflowBuilder<object> builder)
        {
            builder
                .StartWith<HelloWorld>()
                .Then<GoodbyeWorld>();
        }
    }

    public class EdcDataWorkflow : IWorkflow<EdcData>
    {
        public string Id => "EdcDataWorkflow";

        public int Version => 1;

        public void Build(IWorkflowBuilder<EdcData> builder)
        {
            builder
                .StartWith<HelloWorld>()
                .If(data => data.Id < 3).Do(then => then
                        .StartWith<PrintMessage>()
                            .Input(step => step.Message, data => "Passed Id is less than 3")
                )
               .If(data => data.Id < 5).Do(then => then
                        .StartWith<PrintMessage>()
                            .Input(step => step.Message, data => "Passed Id is less than 5")
                )
                .Then<GoodbyeWorld>();
        }
    }

  示例結果很簡單:

  (1)api/values

  

  (2)api/values/1

  

四、小結

  Workflow-Core是一個適合.NET Core的優秀的輕量級工作流引擎,對於小型工作流和責任鏈類型的需求開發很適合,可以節約大量時間避免重復造輪子,將時間主要花在業務邏輯上面。當然,這里演示的示例只是眾多功能特性中的一小部分,我只是選取了我用到的部分而已,大家有興趣的話可以去GitHub上先給個star再仔細研究其wiki文檔,應用到自己的項目中去。

  示例代碼:https://github.com/EdisonChou/EDC.WorkflowCore.Sample

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM