如果没有应用程序回收,在 ASP.NET 中使用后台工作人员有啥好处?
Posted
技术标签:
【中文标题】如果没有应用程序回收,在 ASP.NET 中使用后台工作人员有啥好处?【英文标题】:Are the benefits to using background workers in ASP.NET if there isn't app recycling?如果没有应用程序回收,在 ASP.NET 中使用后台工作人员有什么好处? 【发布时间】:2020-07-17 21:04:32 【问题描述】:背景:我有一个简单的 ASP.NET Core 3.1 站点。在极少数情况下(每周三到四次),用户可能会填写触发电子邮件发送的表单。
我不想在运行“发送电子邮件”操作时延迟页面响应(即使它只需要一两秒钟),所以从我读过的所有内容来看,似乎应该处理的代码email 应该是background worker/hosted service,并且 Razor 页面代码应该将要发送的数据对象放在由后台服务监控的集合中。
我不完全理解的是为什么这在 现代 ASP.NET Core 中是必要的。
如果我在普通的 C# 应用程序(不是 ASP)中执行此操作,我只需将“发送电子邮件”方法设为异步(它使用具有异步方法的 MailKit),并在不等待的情况下调用异步方法,从而允许在允许响应线程继续的同时在线程池上完成工作。
但是现有的答案和博客文章说,由于 IIS 可以重新启动 ASP 进程(应用程序池回收),因此在 ASP is dangerous 中调用异步方法而无需等待。
然而,我读过的大多数内容都说应用程序回收是旧 ASP 的产物,当时内存泄漏很常见,而在 .Net Core 上并不是真正的东西。此外,许多 ASP 应用程序甚至不再托管在 IIS 中。
此外,据我所知,IHostedService/Background Worker 对象并没有做任何特别的事情——它们似乎没有添加任何额外的线程;它们看起来就像单例,有额外的环境启动和关闭通知。
所以:
在 ASP.NET Core 中调用即发即弃异步方法是否仍被认为是不好的做法,尤其是在即发即忘任务短暂存在的情况下?如果是这样,为什么? [请参阅下面的编辑以进行说明] 除了关闭通知之外,还有什么理由认为后台服务比借用托管线程池线程(通过 Task.Run 或 QueueBackgroundWorkItem)更好?唤醒后台服务(如果它正在等待将对象放入集合中)不会以相同的方式消耗池线程吗?编辑:我承认,当有可能终止操作时,启动任务并向用户报告成功是一种糟糕的形式。收到关闭通知并能够完成任务是有好处的。
也许更好的问题是,在现代 ASP(在 IIS 或 Kestrel 上)中是否仍然存在循环的旧行为?是否有其他原因可能会触发有序关闭(服务器关闭/手动停止除外)?
【问题讨论】:
您是否打算再次部署您的应用程序(例如更改代码)?如果是这样,你有你的答案。 这仍然是不好的做法,因为您不能轻易地通过该方法抛出异常。不幸的是,如果将使用 IIS,它仍然可以退出您的线程并在其中运行方法。 您没有提到空闲关机,只是应用程序池回收,这使得这个问题在场景覆盖方面不太完整。此外,工作进程可能会在未处理的异常(来自或不来自那些后台任务)下崩溃。 【参考方案1】:我仍然认为这是一种糟糕的做法。
这里以及引用的帖子中的主要关注点主要是关于任务完成的承诺。
在不知道幽灵后台任务的情况下,运行时将无法通知任务正常停止。这可能会也可能不会导致严重问题,具体取决于终止发生时的任务状态。
使用 fire forget 任务通常意味着,当进程重新启动时,您的任务有不得不重新开始的风险。有时由于上下文丢失,这是不可能的。想象一下,您的 Fire-forget 任务正在使用 Web 请求提供的参数调用另一个 Web API。如果进程重新启动,参数可能会从内存中清除。
请记住,回收并不总是由 IIS/服务器触发。它也可能由人触发。假设您的应用程序遇到内存泄漏问题,并且您可能希望每 1 小时回收一次应用程序进程作为临时缓解措施。然后你需要确保你不会破坏你的后台任务。
在托管方面 - 仍然可以托管 ASP.Net Core 应用程序in-process,其中应用程序池在配置的时间段或默认 29 小时后由 IIS 回收.
就生命周期而言 - 托管服务是您注册到 DI 的类型,因此可以使用 DI 功能,例如,this built-in hosted service 实现 IDisposable
,这意味着可以进行适当的清理关闭时。
坦率地说,后台任务和托管服务都可以让您一劳永逸。但是,当您需要可靠性和弹性时,托管服务会胜出。
【讨论】:
OK - 因此在进程内托管时 IIS 中仍然存在循环。这回答了前半部分(手动循环的例子是一个很好的例子)。但是,对于问题的后半部分,除了通知之外,托管服务是否在做任何特殊的事情(例如,如果我在工作线程中侦听应用程序生命周期事件并对其做出反应,那实际上是否相同,或者托管服务的管理方式不同)? 我不认为主机会为托管服务做些什么特别的事情,你可以在这里查看源代码:github.com/dotnet/aspnetcore/blob/master/src/Hosting/Hosting/… 如果你监听生命周期事件等并相应地处理你的任务,它几乎是在自己构建另一个托管服务框架,为什么不直接使用现有代码:) 这是一个很好的观点——那么我会考虑这个问题。谢谢!【参考方案2】:为了回答您问题的后半部分,该应用将等待所有托管服务的 StopAsync
方法完成,然后再关闭。只要您在托管服务中await
您的Task
s,这实际上意味着您可以假设您的Task
s 将被允许在应用程序关闭之前完成运行。该应用程序仍可能被强制关闭,在这种情况下,不再有任何保证。
如果您需要更多关于后台任务的保证,您应该将它们移动到单独的进程中运行。您可以使用 Runly 之类的东西,以便更轻松地将功能分解到后台作业中。它还使provide real-time feedback to the user 变得容易,这样当您在后台运行时说“一切都已完成”时,您不会对用户撒谎。
全面披露:我共同创立了 Runly。
【讨论】:
以上是关于如果没有应用程序回收,在 ASP.NET 中使用后台工作人员有啥好处?的主要内容,如果未能解决你的问题,请参考以下文章
asp.net 在调试模式下,socket服务不会停止,但部署到IIS后,过段时间socket服务会停止,而且没有异常
用于 Asp.Net Core 的 Kestrel 网络服务器 - 是不是在一段时间后回收/重新加载
asp.net core定时任务保持存活,不会被回收。asp.net core回收事件