繁体   English   中英

通过 REST 请求启动运行时间极长的进程

[英]Start extremely long running processes through a REST request

我在一家自动化公司工作,所以我们为工业自动化创建流程。 以前这种自动化是在机器方面完成的,但我们正在慢慢过渡到使用 c# 控制机器。

在我目前的项目中,一天的制作大约需要 2 个小时。 工厂的操作员有一个我们在 c# 中使用 asp.net 核心 MVC 创建的 Web 界面,他们可以在其中启动/暂停/停止此生产过程。

当开始这个过程时,我们等待控制器中的一个函数,它基本上是一个控制这个 2 小时长的生产过程的 while 循环。

现在的问题是,当我发出 REST 请求以开始生产时,此请求需要 2 小时才能完成,我希望此请求立即完成,并且生产过程在我的 asp.net 核心应用程序的后台开始。

首先,我想我可以省略等待,只需在我的控制器中执行此操作(简化代码):

_ = _productionController.StartLongProcess(); // This contains the while loop
return Ok();

但是由于 _productionController 是有作用域的,并且它的所有依赖项也是如此,因此当方法返回时这些会立即被处理掉,例如我无法再访问我的数据库。

该流程应该能够不断地与我们的数据库对话,以保存有关生产流程的信息,以防万一出现故障,这样我们就可以从中断的地方继续进行。

我现在要问你的问题是,我们是否以错误的方式解决这个问题? 我想在 asp.net 控制器中启动这些长时间运行的进程是不好的做法。

即使 REST 请求已经结束,我如何确保在这个长时间运行的过程中始终可以访问我的 DatabaseContext。 仅为此方法创建单独的范围?

从 ASP.NET Core 2.1 开始,执行此操作的正确方法(在 asp.net 中)是扩展BackgroundService (或实现IHostedService )。

传入的请求可以告诉后台服务启动长时间运行的操作并立即返回。 您当然需要处理在现有请求完成之前发送重复请求或发送新请求的情况。

文档页面有一个示例,其中BackgroundService读取队列的命令并一次处理一个。

即使 REST 请求已经结束,我如何确保在这个长时间运行的过程中始终可以访问我的 DatabaseContext。 仅为此方法创建单独的范围?

是的,创建一个单独的范围。

我现在要问你的问题是,我们是否以错误的方式解决这个问题? 我想在 asp.net 控制器中启动这些长时间运行的进程是不好的做法。

我们过去做过类似的事情。 只要容错(尤其是 wrt 应用程序重新启动)和幂等性内置到长时间运行的操作的逻辑中,您就应该很高兴。

REST 请求预计很短,最多几秒钟。 所以这里的最佳实践是将长时间运行的任务卸载到后台服务,并返回一个令牌,如果操作已经完成,您可以在其中轮询服务。

后台服务可以是 Net Core 中的 BackGroundWorker。 这很容易,但不是真正的容错,所以某种数据库和重试逻辑可能是好的。

如果您在 Intranet 中,您还可以转而使用固有的异步协议,例如 RabbitMQ,您可以在其中发送 StartOperation 消息,然后在流程完成时接收 Started 消息。

以下是我对您发布的问题的理解:

  1. 您想通过 Rest api 调用发起长时间运行的调用
  2. 您想使用异步调用,但不确定如何为长时间运行的调用维护数据库上下文,该调用在操作期间定期用于数据库通信

几个要点:

  1. 大多数情况下,您不清楚异步调用的工作
  2. 当您进行 Async 调用时,它使用状态机存储当前线程同步上下文以保持连续性,它不会阻塞任何线程池线程,它利用基于硬件的并发性
  3. 可以在后端使用ConfigureAwait(false)避免在当前同步上下文中显式重入,这对性能更好
  4. 只有通过 Async 调用才能挑战真正的 async 整个链需要从入口点启用 Async,否则无法获得好处,如果您在任何地方使用Task.WaitTask.ResultTask.Result甚至可能导致死锁ASP.NET

关于长时间运行的操作,以下是选项

  1. 上面建议的简单异步调用,虽然它可以帮助您进行大量异步调用(因此可扩展性),但是如果客户端消失并且无法恢复操作状态,上下文将丢失
  2. 您可以进行即发即弃调用,并使用像 ASP.Net SignalR 这样的机制,它类似于网络上的IObservable ,可以帮助在处理完成时通知客户端
  3. 最好的选择是使用像 Rabbit MQ 这样的消息队列,它不会冒客户端宕机的风险,它充当生产者消费者并且可以在客户端出现时通知,在这种情况下,MQ 可以在进程完成时得到通知,并且因此可以通知客户。 MQ 可以异步方式用于传入和响应消息

如果客户端想要定期上来并检查请求的状态,那么数据库持久性很重要,它可以定期更新,并且可以检查长时间运行的进程的状态。

另一种选择是使用 Hangfire。 它将允许您将要执行的工作排入持久存储,例如 SQL Server、MSMQ、Redis,具体取决于您的基础架构中的内容。 然后,该作业将由一个也可以在 ASP.NET 进程或 Windows 服务中运行的工作人员接手。 它也是分布式的,因此您可以运行多个工作程序实例。 还支持重试失败的作业,并有一个仪表板来查看作业。 最棒的是,它是免费的!

var jobId = BackgroundJob.Enqueue(() => ExecuteLongRunningProcess(parameter1));

https://www.hangfire.io/

我现在要问你的问题是,我们是否以错误的方式解决这个问题? 我想在 asp.net 控制器中启动这些长时间运行的进程是不好的做法。

一般来说,是的。 理想情况下,ASP.NET 服务内部没有任何长时间运行的进程 - 或者至少,没有无法轻松快速关闭的长时间运行进程。

通过添加具有单独后台处理器的持久队列,最可靠地实现 HTTP 请求之外的工作(即请求外部代码)。 “后台处理器”可以是 Win32 服务,甚至可能在同一台机器上。 在这种架构下,HTTP 请求将请求放入队列,处理器从队列中选取请求并执行它们。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM