一个适合于.NET Core的超轻量级工作流引擎:Workflow-Core

一个适合于.NET Core的超轻量级工作流引擎:Workflow-Core - 腾讯云开发者社区-腾讯云 (tencent.com)

一、关于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:

publicclassHelloWorldWorkflow: IWorkflow
    {public string Id=>"HelloWorld";public int Version=>1;publicvoidBuild(IWorkflowBuilder<object> builder){
            builder
                .StartWith<HelloWorld>().Then<ActiveWorld>().Then<GoodbyeWorld>();}}

复制

  这里定义了一个HelloWorldWorkflow,其版本号为1,它有3个步骤:HelloWorld、ActiveWorld和GoodbyeWorld,会依次执行。

  (2)定义三个继承自StepBody类的步骤类:

publicclassHelloWorld: StepBody
    {public override ExecutionResult Run(IStepExecutionContext context){
            Console.WriteLine("Hello World!");return ExecutionResult.Next();}}publicclassActiveWorld: StepBody
    {public override ExecutionResult Run(IStepExecutionContext context){
            Console.WriteLine("I am activing in the World!");return ExecutionResult.Next();}}publicclassGoodbyeWorld: StepBody
    {public override ExecutionResult Run(IStepExecutionContext context){
            Console.WriteLine("Goodbye World!");return ExecutionResult.Next();}}

复制

  (3)ServiceCollection中注入Workflow-Core相关组件

privatestatic IServiceProvider ConfigureServices(){
        IServiceCollection services =newServiceCollection();
        services.AddLogging();// WorkflowCore需要用到logging service
        services.AddWorkflow();var serviceProvider = services.BuildServiceProvider();return serviceProvider;}

复制

  (4)在Program.cs的Main方法中获取到注入的host并执行工作流

publicstaticvoidMain(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所示:

publicclassIfStatementWorkflow: IWorkflow<MyData>{public string Id=>"if-sample";public int Version=>1;publicvoidBuild(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的定义如下:

publicclassMyData{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()扩展方法

publicvoidConfigureServices(IServiceCollection services){
            services.AddWorkflow();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);}

复制

  (2)初始化:

publicvoidConfigure(IApplicationBuilder app, IHostingEnvironment env){.......      
            app.UseWorkflow();}

复制

  扩展方法如下:

publicstaticclassConfigureExtensions{publicstatic 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]publicclassValuesController: ControllerBase
    {private IWorkflowController _workflowService;publicValuesController(IWorkflowController workflowService){
            _workflowService = workflowService;}// GET api/values[HttpGet]publicasync Task<IEnumerable<string>>Get(){await _workflowService.StartWorkflow("EdcWorkflow");returnnewstring[]{"EdcWorkflow v1"};}// GET api/values/5[HttpGet("{id}")]publicasync Task<string>Get(int id){await _workflowService.StartWorkflow("EdcDataWorkflow",newEdcData(){ Id = id });return"EdcDataWorkflow v1";}}

复制

  这两个Workflow的定义如下:

publicclassEdcWorkflow: IWorkflow
    {public string Id=>"EdcWorkflow";public int Version=>1;publicvoidBuild(IWorkflowBuilder<object> builder){
            builder
                .StartWith<HelloWorld>().Then<GoodbyeWorld>();}}publicclassEdcDataWorkflow: IWorkflow<EdcData>{public string Id=>"EdcDataWorkflow";public int Version=>1;publicvoidBuild(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文档,应用到自己的项目中去。

<think>好的,我现在需要帮助用户解决关于RedisCommandExecutionException: ERR redis temporary failure的问题。首先,我要回想一下用户提到的错误信息,以及他们之前提到的阿里云Redis集群的问题。用户之前遇到了Lua脚本在集群中的问题,现在这个新的错误可能与之相关,但需要仔细分析。 首先,我需要理解ERR redis temporary failure的具体含义。这个错误通常表示Redis在某个时刻无法处理请求,可能是临时性的问题。接下来,我需要列举可能的原因,比如主从切换、内存不足、网络问题、配置问题、集群状态异常、客户端处理不当等。 然后,针对每个可能的原因,我需要给出相应的解决方案。例如,主从切换时客户端需要有重试机制,内存不足需要检查内存使用情况并优化数据,网络问题需要排查连接稳定性,配置不当需要调整timeout和重试策略,集群状态异常需要检查节点健康,客户端需要优化重试逻辑等。 同时,用户之前提到的Lua脚本问题可能与集群模式下的键未正确使用有关,但当前的错误是临时性的,可能和集群稳定性有关,所以需要区分开来。需要确保用户没有混淆这两个错误的原因。 还要参考用户提供的引用内容,虽然之前的问题是关于Lua脚本的,但可能阿里云Redis集群的一些限制或配置会影响当前的问题。例如,集群的自动故障转移可能导致临时不可用,客户端需要适应这种情况。 最后,生成相关问题,帮助用户进一步排查,比如如何监控Redis集群状态、Lettuce客户端配置、内存优化方法等。确保回答结构清晰,步骤详细,用户能够逐步排查问题。</think>### 关于RedisCommandExecutionException: ERR redis temporary failure的分析与解决方案 #### 一、错误原因分析 1. **集群主从切换(故障转移)** Redis集群在进行主从切换时,可能出现短暂的服务不可用状态,此时客户端请求会触发临时故障报错[^1]。例如: $$ T_{\text{failover}} = T_{\text{detection}} + T_{\text{election}} + T_{\text{sync}} $$ 2. **内存溢出或资源限制** 当Redis实例内存达到maxmemory限制,且无有效淘汰策略时,可能导致临时写入失败。可通过`info memory`命令验证内存使用率。 3. **网络波动或连接池耗尽** 网络闪断或客户端连接池(如Lettuce)配置不当(如maxActive=8)会导致突发流量下的连接不足。 4. **集群状态异常** 节点通信异常(如CLUSTERDOWN)、槽位未完全分配时,会出现临时故障提示。 #### 二、解决方案步骤 1. **客户端重试机制优化** 在Lettuce客户端配置自动重试策略: ```java ClientOptions options = ClientOptions.builder() .autoReconnect(true) .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS) .socketOptions(SocketOptions.builder().connectTimeout(Duration.ofSeconds(5)).build()) .build(); ``` 建议配合指数退避算法实现重试,例如: $$ \text{delay} = \text{base} \times 2^{(\text{attempt}-1)} $$ 2. **集群状态检查** 执行`redis-cli --cluster check <host>:<port>`确认集群所有节点处于`ok`状态,槽位分布完整。 3. **资源监控与扩容** - 通过`redis-cli info`监控内存、连接数等关键指标 - 修改maxmemory-policy为`allkeys-lru`(根据业务场景选择) - 阿里云控制台开启"自动扩容"功能 4. **慢查询排查** 使用`slowlog get 10`分析阻塞命令,优化BigKey、复杂Lua脚本等(注:集群模式下Lua脚本需确保所有key在同一slot[^1]) #### 三、典型场景示例 某电商平台在秒杀活动中出现该错误,最终定位原因为: 1. Lettuce默认未启用重试机制 2. 集群节点内存达到98%使用率 3. 存在多个1MB大小的Hash结构BigKey 优化措施: - 增加客户端重试策略 - 对Hash结构进行分片存储 - 将淘汰策略改为`volatile-ttl`
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值