WebAPI中的长时间运行任务

34

这是我的问题:我需要在ApiController内调用多个第三方方法。这些方法的签名为Task DoSomethingAsync(SomeClass someData, SomeOtherClass moreData)。我希望这些调用在ApiController将数据发送回客户端后继续在后台运行。当DoSomethingAsync完成时,我想进行一些日志记录,可能还要保存一些数据到文件系统中。我该如何做?我更喜欢使用异步/等待语法。


你的任务中有类似于.ContinueWith的东西,这是你可以进行日志记录的地方。 - T.S.
当你说“ApiController将数据发送回客户端后”,你是指连接也关闭了吗?客户端是否可以在保持连接的同时从服务器接收数据? - Alex S
1
是的,连接已经关闭。 - TEst16
1
请查看 http://hangfire.io/。 - MetaGuru
针对 .Net Core 2.x,可以查看 IHostedService。这篇博客提供了一个很好的例子:https://www.stevejgordon.co.uk/asp-net-core-2-ihostedservice。与 Hangfire 概念类似。 - slasky
5个回答

37

好消息,.NET 4.5.2 中有一个被称为 QueueBackgroundWorkItem API 的新解决方案。它非常简单易用:

HostingEnvironment.QueueBackgroundWorkItem(ct => DoSomething(a, b, c));

这里有一篇详细描述的文章。

https://blogs.msdn.microsoft.com/webdev/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-background-processes-in-asp-net/

另外,这里还有另一篇文章提到了几种在此主题未提及的方法。 http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx


5
如果长时间运行的任务实际上并不需要很长时间完成,即可以在不到90秒内完成,那么QueueBackgroundWorkItem可能是一个解决方案。如果它确实需要很长时间才能完成,则需要另一种解决方案(比如将其移交给Windows服务)。 - Torben Junker Kjær
我无法在 Windows 服务(后端数据层)中的自托管 WebAPI 中使其工作。 - William T. Mallard
1
请注意,QueueBackgroundWorkItem 在 .NET Core (.NET 5+) 中已经不存在。 - Sedat Kapanoglu

30

你几乎从不想这样做。这几乎总是一个大错误。

ASP.NET(以及大多数其他服务器)的工作原理是,一旦所有请求完成,关闭服务是安全的。因此,您无法保证日志记录已完成,或者数据已写入磁盘。特别是对于磁盘写入,您的写入完全可能会损坏。

话虽如此,如果你绝对确定要实现这个极度危险的设计,可以使用BackgroundTaskManager (来自我的博客)

更新: 我已经写了一个博客系列,详细介绍了一个适合请求外代码的正确解决方案。简而言之,您真正想做的是将请求外代码移出 ASP.NET。引入可持久化队列和独立处理器;ASP.NET 控制器动作将一个请求放置到队列中,独立处理器将读取请求并执行它们。该“处理器”可以是 Azure Function/WebJob、Win32 Service 等。


2
在 Azure-WebJobs 出现之前,你必须确保代码正确。现在你可以使用 Azure-WebJobs 来完成这个任务。Worker 角色是最稳健的方法。 - RickAndMSFT
我认为这是一种可以接受的权衡,例如:调试日志记录、性能记录、事件跟踪(使用外部服务)。在所有这些情况下,“fire-and-forget”语义都是可以接受的,如果在服务关闭的情况下可能会丢失单个事件,我们并不真正关心。或者我有什么遗漏吗? - Johannes Rudolph
1
@JohannesRudolph:我认为主要用例是缓存更新。如果您接受日志可能没有所有数据,那么这是一种可接受的记录方式。我会将“事件跟踪”解释为业务需求,因此不适合此用例。如果“事件跟踪”不重要,则“调试日志记录”、“性能记录”和“事件跟踪”只是三种不同类型的日志记录。请注意,ASP.NET 4.5.2现在内置了类似的功能。 - Stephen Cleary

17

Stephen解释了为什么在ApiController中启动基本上是长时间运行的fire-and-forget任务是一个坏主意。

也许你应该创建一个独立的服务来执行那些fire-and-forget任务。这个服务可以是不同的ApiController,一个队列后面的工作程序,任何可以单独托管并具有独立生命周期的东西。

这将使得管理不同任务生命周期更加容易,并且将长时间运行的任务的责任与ApiController的核心责任分离开来。


1
另一个为什么单独拥有服务更好的原因是可以更容易地进行横向扩展。例如,也许你发现需要更多的服务器来运行长时间运行的任务,但不需要更多的服务器来运行网站。通过在开始时将它们分开,你可以更好地定制你的横向扩展解决方案。 - jt000
如何实现它? - Kiran Ahir
1
@KiranAhir,文档中有解释:ASP.NET Core中的托管服务后台任务。 在Web应用程序中,您可以创建BackgroundService以处理长时间的任务。 您还可以创建一个具有Background服务的单独的Web API项目,以接收来自前端Web应用程序的请求。 还有一个Worker Service模板,可创建独立的后台服务。 - Panagiotis Kanavos

4

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接