本文将和大家推荐我团队开源的 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. 详解SSL证书系列(5)SSL证书为什么不能好多年签一次呢

    上一篇介绍了详解SSL证书系列(4)免费的SSL证书和收费的证书有什么区别,这一篇我们继续了解一下我们申请的SSL证书为什么不能好多年签一次呢,这样不是更省事吗? SSL证书最多只能签发一年,一年到期 ...

  2. 记录-new Date() 我忍你很久了!

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 大家平时在开发的时候有没被new Date()折磨过?就是它的诸多怪异的设定让你每每用的时候,都可能不小心踩坑.造成程序意外出错,却一下子 ...

  3. springboot整合视频点播

    1 //上传视频到阿里云 2 @Override 3 public String uploadAyl(MultipartFile file) { 4 try { 5 //accessKeyId,acc ...

  4. FastJson反序列化1-FastJson基础使用及反序列化流程分析

    1.FastJson简介及使用 fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到Java ...

  5. DARTS:基于梯度下降的经典网络搜索方法,开启端到端的网络搜索 | ICLR 2019

    DARTS是很经典的NAS方法,它的出现打破了以往的离散的网络搜索模式,能够进行end-to-end的网络搜索.由于DARTS是基于梯度进行网络更新的,所以更新的方向比较准确,搜索时间相当于之前的方法 ...

  6. KingbaseES V8R3 备份恢复案例 -- sys_rman物理备份异机恢复

    案例说明: 在生产环境通过sys_rman执行了物理备份后,需要在异机构建测试环境,本案例描述了通过物理备份异机恢复的详细过程及操作. 适用版本: KingbaseES V8R3 节点信息: [kin ...

  7. KingbaseES Json 系列三:Json数据操作函数一

    KingbaseES Json 系列三--Json数据操作函数一(JSONB_EACH,JSONB_EACH_TEXT,JSONB_OBJECT_KEYS,JSONB_EXTRACT_PATH,JSO ...

  8. Debug模式调试技巧

    Debug模式调试技巧 断点管理窗口  菜单栏方式 Run -> view breakpoints  Actions方式 双击shift调出Actions窗口,输入view breakpoint ...

  9. #裴蜀定理#洛谷 2520 [HAOI2011]向量

    题目 分析 首先若 \(a,b\) 都为 0 要特判. 若 \(\begin{cases}x=pa+qb+p'a+q'b\\y=qa+pb-q'a-p'b\end{cases}\) 合并同类项可以得到 ...

  10. 【Kotlin】函数

    1 常规函数 1.1 无参函数 fun main() { myFun() } fun myFun() { println("myFun") // 打印: myFun } 1.2 有 ...