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

时间:2019-06-06 08:19:47

标签: c# rest asp.net-core

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

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

启动过程时,我们在控制器中等待一个函数,该函数基本上是一个while循环,用于控制此2小时的生产过程。

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

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

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

但是由于_productionController的作用域以及它的所有依赖关系也都存在,所以当方法返回时,这些将立即被处理掉,例如,我无法再访问我的数据库。

该流程应该能够连续地与我们的数据库进行对话,以在发生某些故障时保存有关生产流程的信息,这样我们就可以始终从上次中断的地方开始。

我现在对您的问题是,我们是否以错误的方式处理此问题?我认为在asp.net控制器中启动这些长时间运行的进程是不好的做法。

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

5 个答案:

答案 0 :(得分:2)

REST请求应该很短,最多几秒钟。 因此,最好的做法是将长时间运行的任务卸载到后台服务,并返回令牌,如果操作已经完成,则可以在该令牌中轮询该服务。

后台服务可能是Net Core中的BackGroundWroker。这很简单,但并不是真正的容错,因此某种形式的db和retry逻辑可能会很好。

如果您在Intranet中,则还可以移动到类似RabbitMq的固有异步协议,在该协议中,您发送StartOperation消息,然后在该过程完成后对Started消息进行重新显示。

答案 1 :(得分:2)

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

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

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

  

即使在REST请求已经结束的情况下,如何确保在这个长期运行的过程中始终可以访问我的DatabaseContext。仅为此方法创建单独的作用域?

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

  

我现在对您的问题是,我们是否以错误的方式处理此问题?我认为在asp.net控制器中启动这些长时间运行的进程是不好的做法。

过去我们做了类似的事情。只要容错功能(尤其是重新启动w.r.t.应用程序)和幂等性已纳入长期运行的逻辑中,那么您就应该行得通。

答案 2 :(得分:1)

  

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

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

要点:

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

关于长期运行,以下是选项

  1. 如上所述的简单异步调用,尽管它可以帮助您进行大量异步调用(因此具有可伸缩性),但是如果客户端离开并且无法获得操作状态的回复,则上下文将丢失
  2. 您可以拨打电话忘记通话,并使用类似ASP.Net SignalR的机制,该机制类似于网络上的IObservable,并可以在处理完成时帮助通知客户端
  3. 最好的选择是使用像Rabbit MQ这样的消息传递队列,它不会冒着客户端崩溃的风险,它充当生产者使用者,并且可以在客户端启动时通知,在这种情况下,可以在客户端启动时通知MQ。处理完成,因此可以通知客户。 MQ可以异步方式用于传入消息和响应消息

如果客户端希望定期出现并检查请求的状态,则数据库持久性很重要,可以定期更新数据库持久性,并且可以检查长时间运行的状态是什么。

答案 3 :(得分:1)

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

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

https://www.hangfire.io/

答案 4 :(得分:0)

<块引用>

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

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

在 HTTP 请求(即请求外在代码)之外进行工作最可靠地由 adding a durable queue with a separate background processor 实现。 “后台处理器”可以是 Win32 服务,甚至可能在同一台机器上。在这种架构下,HTTP 请求将请求放入队列,处理器从队列中提取请求并执行它们。