一个.NET开发的轻量级工作过程管理库

2023-10-23 14:21:53 浏览数 (1)

今天给大家一个.NET开发的轻量级工作过程管理库LightWorkFlowManager,它适合任何需要执行工作过程的应用逻辑,可以方便将多个工作过程拼凑起来,且自动集成重试和失败处理,以及日志和上报功能。

这个 LightWorkFlowManager 库在功能上和其他的工作过程管理库相比要缺失许多,但胜在轻量。且 LightWorkFlowManager 最重要的卖点就是对调试友好,适合用在本身复杂度就比较好的逻辑情况下,可以有效降低引入工作过程管理模块本身带来的额外复杂度。

使用方法

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

代码语言:javascript复制
// 每个任务一个 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 的过程上下文缓存里面

代码语言:javascript复制
record InputType();

record OutputType();

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

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

代码语言:javascript复制
 var result = await workerManager
.GetWorker<FooWorker>()
.RunAsync();

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

代码语言:javascript复制
var result = await workerManager.RunWorker<FooWorker>();

机制和功能

工作器参数

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

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

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

代码语言:javascript复制
// 例子1:先获取工作器,再赋值给到工作器的执行方法
await Manager
  .GetWorker<FooWorker>()
  .RunAsync(new InputType());

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

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

代码语言:javascript复制
// 以下例子将当前上下文里的 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 工作器,定义如下:

代码语言:javascript复制
class Worker2 : MessageWorker<InputType, OutputType>
{
    protected override ValueTask<WorkerResult<OutputType>> DoInnerAsync(InputType input)
    {
        return SuccessTask(new OutputType());
    }
}

record InputType();

record OutputType();

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

代码语言:javascript复制
class Worker1 : MessageWorkerBase
{
    public override async ValueTask<WorkerResult> Do(IWorkerContext context)
    {
        await Manager
            .GetWorker<Worker2>()
            .RunAsync(new InputType());

        return WorkerResult.Success();
    }
}

委托工作器

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

代码语言:javascript复制
 var delegateMessageWorker = new DelegateMessageWorker(_ =>
 {
     // 在这里编写委托工作器的业务内容
 });

 var result = await messageWorkerManager.RunWorker(delegateMessageWorker);

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

代码语言:javascript复制
 await messageWorkerManager.RunWorker((IWorkerContext context) =>
     {
     // 在这里编写委托工作器的业务内容
 });

资源获取方式

https://github.com/dotnet-campus/LightWorkFlowManager

0 人点赞