本文将和大家推荐我团队开源的 LightWorkFlowManager 轻量的工作过程管理库,适合任何需要执行工作过程的应用逻辑,可以方便将多个工作过程拼凑起来,且自动集成重试和失败处理,以及日志和上报功能

这个 LightWorkFlowManager 轻量的工作过程管理库是我所在的团队在 GitHub 上使用最友好的 MIT 协议开源的库,请看 https://github.com/dotnet-campus/LightWorkFlowManager

这个 LightWorkFlowManager 轻量的工作过程管理库现在已经在我团队的正式产品应用上运行有半年了,相对来说比较稳定。使用过程中如果有任何问题,都欢迎在 GitHub 上新建 Issues 进行反馈

世界上有许多现有的工作过程管理,那为什么还重新造了一个?没啥原因,自己造的用的顺手。这个 LightWorkFlowManager 库在功能上和其他的工作过程管理库相比要缺失许多,但胜在轻量。且 LightWorkFlowManager 最重要的卖点就是对调试友好,适合用在本身复杂度就比较好的逻辑情况下,可以有效降低引入工作过程管理模块本身带来的额外复杂度

许多现有的功能齐全的工作过程管理库都是在 ASP.NET Core 上使用的。然而我这里的需求是在客户端应用框架上使用,且大部分情况下是在用户的设备上运行的,许多服务依赖本身是不存在的。尽管许多现有的功能齐全的工作过程管理库都可以通过配置的形式将其切换到单机内运行,然而这样做将在大部分时候不是库的推荐用法,用起来没有那么顺。为此我所在的团队开发了 LightWorkFlowManager 轻量的工作过程管理库,这个库可以很方便在客户端框架上运行,比如在 WPF 应用程序、在 WinForms 应用程序、在 MAUI 应用程序上运行。同时 LightWorkFlowManager 也兼顾 ASP.NET Core 服务本身,特别是依赖注入部分,可以和默认的服务的依赖注入相连接

我将举一个例子来告诉大家这个 LightWorkFlowManager 库所适用的情况。假定我拿到了一个开发任务,这个任务是做 PPT 解析,取出 PPT 文件里面的图片。细分这个任务将包括以下三个步骤:步骤一,获取 PPT 文件。步骤二,调用某个现有帮助方法获取到 PPT 文件里面的图片。步骤三,对拿到的 PPT 文件里面的图片文件进行处理

在步骤一里面,有多个方式获取到 PPT 文件,比如有些是从网上 CDN 服务器上下载的,有些是从磁盘路径获取的,有些是用户拖入的。这些不同的输入获取方式来自于各个业务的不同入口

在步骤三里面,又根据不同的业务情况,需要分为将图片文件进行上传到后台服务器,或者是有些业务需要将这些图片展示到界面上等等

从以上的描述可以看到,我所拿到的是一个可以散落在各个业务里面,看似存在许多不同,却又在逻辑上相似的需求。逻辑上的相似也就是可以强行归类步骤进行约束,逻辑上的重复在于有许多逻辑是完全可复用的,比如记录日志和重试机制。重试机制几乎可以用在以上三个步骤里面,无论是开发新手还是富有经验的开发者都知道,无论是从网上下载内容还是上传文件都需要经过不稳定的网络传输,这个时候因为网络传输失败而执行重试逻辑是十分常见的处理逻辑

假定没有 LightWorkFlowManager 库的辅助,一个可能的实现设计大概如下:设计一个统一的入口,通过入口传入参数判断执行不同的步骤处理逻辑,如下图

通过上图可以很简单就看出来问题,整个的逻辑复杂度将会比较高,原本的业务入口就存在多个,接着进入到 PPT 解析任务里面的统一入口,在 PPT 解析任务里面的统一入口再根据参数分发逻辑到不同的处理逻辑上。整个逻辑的圈复杂度将比较高,同时对于后续如果在步骤一或步骤三再添加更多的业务需求,将会让整个逻辑更加复杂起来。另一方面,如此复杂的逻辑,将会导致代码阅读和调试的困难性,比如难以非常明确的知道整个的调用链,调用链可能过于复杂,比如难以在调试的时候进行重复执行

在有 LightWorkFlowManager 库的辅助下,可以修改一下代码的逻辑设计,也就是不再进行统一的入口的分发,而是让各自的业务串联对应的逻辑,如此可以理顺一条逻辑,保持 PPT 解析任务模块的简单。同时业务方也可以更加清楚 PPT 解析任务的细节

可以看到修改之后的代码,可以让每个逻辑都是顺下来的,不存在逻辑的圈。可以将原本就比较复杂的逻辑,降低其逻辑复杂度。一个串联的顺的逻辑,将对代码阅读以及调试更加友好。且在 LightWorkFlowManager 库的辅助之下,可以尽量减少重复的代码逻辑,比如减少重复的记录日志和失败重试逻辑

在了解 LightWorkFlowManager 库的适用设计之后,接下来将和大家介绍具体的使用方法

按照 dotnet 的惯例,先安装 NuGet 库,请看 https://www.nuget.org/packages/dotnetCampus.LightWorkFlowManager

在开始介绍使用方法之前,需要先介绍一下 LightWorkFlowManager 里面的概念。在 LightWorkFlowManager 里面定义了 MessageWorkerManager 类型,将其作为工作器的管理器,用途就是将各个工作器串联起来,且运行起来,这个 MessageWorkerManager 就是 LightWorkFlowManager 的入口类型

在 LightWorkFlowManager 里面定义了工作器的基础类型就是 MessageWorker 类型,所有的业务上的开发者自定义的工作器都需要直接或间接继承 MessageWorker 类型

了解基础概念之后,接下来就开始编写一个简单的逻辑作为例子

使用方法

1、 创建 MessageWorkerManager 对象。创建 MessageWorkerManager 工作器管理器即可作为承载工作器的框架,请为每次单独的任务创建独立的 MessageWorkerManager 对象

            // 每个任务一个 TaskId 号
string taskId = Guid.NewGuid().ToString();
// 相同类型的任务采用相同的名字,比如这是做 PPT 解析的任务
string taskName = "PPT 解析";
// 提供容器
IServiceScope serviceScope = serviceProvider.CreateScope(); var workerManager = new MessageWorkerManager(taskId, taskName, serviceScope);

2、 定义 Worker 工作器。以下例子代码定义了模拟业务代码的 FooWorker 工作器,可以在 FooWorker 重写的 DoInnerAsync 方法里面编写执行的业务代码。以下代码的 InputType 和 OutputType 分别是工作器的输入和输出类型,每个工作器的输出对象都会自动加入到 MessageWorkerManager 的过程上下文缓存里面

record InputType();

record OutputType();

class FooWorker : MessageWorker<InputType, OutputType>
{
protected override ValueTask<WorkerResult<OutputType>> DoInnerAsync(InputType input)
{
...
}
}

3、 执行 Worker 工作器。 完成类型的定义之后,接下来演示如何通过 MessageWorkerManager 将 FooWorker 运行起来

            var result = await workerManager
.GetWorker<FooWorker>()
.RunAsync();

以上代码也可以简写为以下代码

            var result = await workerManager.RunWorker<FooWorker>();

以上代码运行的前提是先注入 FooWorker 所需的 InputType 类型的对象。下文将告诉注入工作器参数部分的更多细节

机制和功能

工作器参数

工作器参数通过 MessageWorkerManager 工作器管理的 IWorkerContext 上下文读取。每个工作器的可返回值类型都会自动被设置到 IWorkerContext 上下文里面。如此即可自动实现上一个 Worker 的输出作为下一个 Worker 的输入

在每个工作器里面,都可以通过 SetContext 设置上下文信息

在开始执行工作器时,还可以手动设置输入参数,如以下例子

 // 例子1:先获取工作器,再赋值给到工作器的执行方法

            await Manager
.GetWorker<FooWorker>()
.RunAsync(new InputType()); // 例子2: 通过 SetContext 进行设置参数,再执行工作器
await Manager
.SetContext(new InputType())
.RunWorker<FooWorker>();

如果有些工作器之间的输入和输出参数需要进行转换,也可以 SetContext 传入转换委托进行参数转换

 // 以下例子将当前上下文里的 Foo1Type 类型转换为 FooWorker 需要的 Foo2Type 参数
await Manager
.SetContext((Foo1Type foo1) => ConvertFoo1ToFoo2(foo1))
.RunWorker<FooWorker>();

异常中断和重试

每个 Worker 都可以返回 WorkerResult 类型的返回值,可以在返回值里面告知框架层是否当前的 Worker 执行成功。在执行失败时,可以赋值错误码,方便定位调试或输出。在执行失败时,可以返回告知框架层是否需要重试,默认情况下框架都会自动进行重试

中断后续的工作器执行有两个方法:

方法1: 通过返回状态为失败的 WorkerResult 返回值。一旦工作管理器的状态为 IsFail 状态,将会阻止所有的没有标记 CanRunWhenFail 为 true 的工作器的执行。换句话说就是除了哪些不断成功或失败状态都要执行的 Worker 工作器之外,其他的工作器都将不会执行,包括 SetContext 里面的委托转换也不会执行

方法2: 通过抛出异常的方式,通过 dotnet 里面的异常可以让后续逻辑炸掉不再执行

以上两个方法都是框架推荐使用的。框架设计的偏好是如果在重视性能的情况下,尽量使用方法1的方式中断执行。如果是在复杂的业务逻辑,有大量业务逻辑穿插在工作过程之外,则可以方便通过方法2进行中断

在 Worker 里执行其他 Worker 工作器

在一个 Worker 里面,可以执行其他的 Worker 工作器,如此可以比较自由的实现分支逻辑,套娃决定执行工作器

例子如下:

假定有一个 Worker2 工作器,定义如下:

    class Worker2 : MessageWorker<InputType, OutputType>
{
protected override ValueTask<WorkerResult<OutputType>> DoInnerAsync(InputType input)
{
return SuccessTask(new OutputType());
}
} record InputType(); record OutputType();

有另一个 Worker1 工作器,可以在 Worker1 里面执行 Worker2 工作器:

    class Worker1 : MessageWorkerBase
{
public override async ValueTask<WorkerResult> Do(IWorkerContext context)
{
await Manager
.GetWorker<Worker2>()
.RunAsync(new InputType()); return WorkerResult.Success();
}
}

委托工作器

有一些非常小且轻的逻辑,也想加入到工作过程里面,但不想为此定义一个单独的工作器。可以试试委托工作器,如以下代码例子

            var delegateMessageWorker = new DelegateMessageWorker(_ =>
{
// 在这里编写委托工作器的业务内容
}); var result = await messageWorkerManager.RunWorker(delegateMessageWorker);

如果连 DelegateMessageWorker 都不想创建,那也可以直接使用 MessageWorkerManager 的 RunWorker 方法传入委托,如以下代码例子

            await messageWorkerManager.RunWorker((IWorkerContext context) =>
{
// 在这里编写委托工作器的业务内容
});

dotnet 推荐 LightWorkFlowManager 轻量的工作过程管理库的更多相关文章

  1. flutter最简单轻量便捷的路由管理方案NavRouter

    大家好,我是CrazyQ1,今天给大家推荐一个路由管理方案,用的非常不错的,叫nav_router. 项目地址是:https://github.com/fluttercandies/nav_route ...

  2. Tippy.js – 轻量的 Javascript Tooltip 工具库

    工具提示(Tooltip)在网站中的一个小功能,但却有很重要的作用,常用于显示一些温馨的提示信息.如果网站中的工具提示功能做得非常有创意的话能够加深用户对网站印象.Tippy.js 是一款帮助你快速创 ...

  3. 免费申请使用IBM Cloud Lite(轻量套餐) 续

    之前尝试申请了IBM的轻量套餐,过程很简单,操作起来也比较方便,就是能够用到的地方不多,虽说几乎是无限流量且永久免费,我能做的也只是做个小网站 免费用户默认的是轻量应用服务,如果需要功能更多更全的应用 ...

  4. 推荐一个简单、轻量、功能非常强大的C#/ASP.NET定时任务执行管理器组件–FluentScheduler定时器

    在C#WINFORM或者是ASP.NET的WEB应用程序中,根据各种定时任务的需求,比如:每天的数据统计,每小时刷新系统缓存等等,这个时候我们得应用到定时器这个东东. .NET Framework有自 ...

  5. 推荐轻量友好的.NET测试断言工具Shouldly

    Shouldly是一个轻量的断言(Assertion)框架,用于补充.NET框架下的测试工具.Shouldly将焦点放在当断言失败时如何简单精准的给出很好的错误信息. Shouldly在GitHub的 ...

  6. 推荐一款轻量小众却高效免费开源windows热键脚本语言Autohotkey

    写在前面的话 Autohotkey是一款轻量小众但高效免费开源的windows热键脚本语言,游戏操纵.鼠标操作.键盘快捷重定义,快捷短语等等,只有你想不到,没有它做不到,神器中的神器呀,相见恨晚. 安 ...

  7. Vue.js:轻量高效的前端组件化方案

    转发一篇尤老师对vue.js的介绍,了解vue.js的来龙去脉.不过现在已经是2.0了,也有添加一些新的东西,当然有些东西也改了. Vue.js:轻量高效的前端组件化方案 Vue.js 是我在2014 ...

  8. Vue.js:轻量高效的前端组件化方案(转载)

    摘要:Vue.js通过简洁的API提供高效的数据绑定和灵活的组件系统.在前端纷繁复杂的生态中,Vue.js有幸受到一定程度的关注,目前在GitHub上已经有5000+的star.本文将从各方面对Vue ...

  9. OWIN轻量型框架介绍

    OWIN轻量型框架介绍 阅读目录 引言 框架的特色 如何启动 各项功能 静态路由的3种写法 伪静态路由的支持 处理Form表单提交的文件 流式处理Post请求的数据 多种请求类型自动识别 响应处理 请 ...

  10. 【转】Vue.js:轻量高效的前端组件化方案

    摘要:Vue.js通过简洁的API提供高效的数据绑定和灵活的组件系统.在前端纷繁复杂的生态中,Vue.js有幸受到一定程度的关注,目前在GitHub上已经有5000+的star.本文将从各方面对Vue ...

随机推荐

  1. AI助力快速定位数据库难题

    最近很多人都在讨论AI能否替代人类工作的话题,最近笔者正好遇到一个AI帮自己快速定位问题的实例,分享给大家,一起来切身感受下AI对于解决数据库问题的价值吧. 事情的经过是这样,有个朋友咨询我,说他最近 ...

  2. 三维模型3DTile格式轻量化在数据存储的重要性分析

    三维模型3DTile格式轻量化在数据存储的重要性分析 三维模型3DTile格式轻量化在数据存储中占有重要地位.随着科技的不断发展,尤其是空间信息科技的进步,人们对于三维地理空间数据的需求日益增长.然而 ...

  3. 记录--使用率比较低的10个Web API

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 avaScript中有些API可能使用率比较低,下面我们逐一介绍它们的用法和使用场景. 至于标题,主要是想让你进来看看,兄弟们别打我! B ...

  4. 《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南 - 第7章

    本章勘误: 暂无,等待细心的你告诉我哦. 本章注解: 暂无 本章释疑: 暂无,等待你的提问 致谢: MVP 林德熙 MVP 吕毅 sPhinX 相关链接 试读记录

  5. 关于使用SSM+JSP开发时setter、getter隐式调用问题的小结

    [版权声明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://www.cnblogs.com/cnb-yuchen/p/17977495 出自[进步*于辰的博客] 之前使用SSM ...

  6. hdfs disk balancer 磁盘均衡器

    目录 1.背景 2.hdfs balancer和 hdfs disk balancer有何不同? 3.操作 3.1 生成计划 3.2 执行计划 3.3 查询计划 3.4 取消计划 4.和disk ba ...

  7. #分治,Kruskal#洛谷 3206 [HNOI2010]城市建设

    题目 动态改边权求最小生成树 \(n\leq 2*10^4,m\leq 5*10^4,q\leq 5*10^4\) 分析 乍一看是线段树分治,但有一个很大的问题就是局部的Kruskal不一定是最后的选 ...

  8. 使用OHOS SDK构建tinyexr

    参照OHOS IDE和SDK的安装方法配置好开发环境. 从github下载源码. 执行如下命令: git clone https://github.com/syoyo/tinyexr.git 进入源码 ...

  9. 看你能解锁哪些新身份?OpenHarmony大使、MVP、金码达人在线申报

  10. 王莉:将开发文档英文化和本地化,我们努力让OpenHarmony走向全球

    编者按:在 OpenHarmony 生态发展过程中,涌现了大批优秀的代码贡献者,本专题旨在表彰贡献.分享经验,文中内容来自嘉宾访谈,不代表 OpenHarmony 工作委员会观点. 王莉 华为技术有限 ...