【问题标题】:C# 5 .NET MVC long async task, progress report and cancel globallyC# 5 .NET MVC 长异步任务、进度报告和全局取消
【发布时间】:2013-11-07 13:45:52
【问题描述】:

我使用 ASP.Net MVC 5,我有一个长时间运行的操作,它必须轮询 Web 服务、处理数据并将它们存储在数据库中。

为此,我想使用 TPL 库来异步启动任务。

但我想知道如何做 3 件事:

  • 我想报告此任务的进度。为此,我考虑 SignalR
  • 我希望能够离开我开始执行此任务的页面,并能够报告整个网站的进度(从左侧的面板,但这没关系)
  • 我希望能够全局取消此任务(从左侧面板)

我对所涉及的所有技术都了解不少。但我不确定实现这一目标的最佳方法。

有人可以帮助我了解最佳解决方案吗?

【问题讨论】:

标签: asp.net-mvc task-parallel-library signalr c#-5.0


【解决方案1】:

您希望运行长时间运行的工作,而用户可以离开启动工作的页面,这意味着您需要“在后台”运行这项工作。它不能作为常规 HTTP 请求的一部分执行,因为用户可能随时通过导航或关闭浏览器来取消他的请求。事实上,这对您来说似乎是一个关键场景。

Background work in ASP.NET is dangerous.你当然可以成功,但要做好并不容易。此外,工作进程可能因多种原因退出(应用程序池回收、部署、机器重启、机器故障、堆栈溢出或无关线程上的 OOM 异常)。因此,请确保您的长期工作能够容忍中途中断。您可以降低发生这种情况的可能性,但永远不要排除这种可能性。

您可以通过将所有工作包装在事务中来使您的代码在任意终止时安全。当然,这仅在您不引起非事务性副作用(例如更改状态的 Web 服务调用)时才有效。在这里不可能给出一个普遍的答案,因为在存在任意终止的情况下实现安全性在很大程度上取决于要完成的具体工作。

这是我过去使用过的一种可能的架构:

  • 当有工作进入时,您将所有必要的输入数据写入数据库表并向客户端报告成功。
  • 您需要一种方法来启动工人从事该工作。您可以为此立即启动一项任务。您还需要定期检查以查找未启动的工作,以防应用在添加工作项后但在为其启动任务之前退出。让 Windows 任务计划程序每分钟调用一次应用中的秘密 URL。
  • 当您开始处理某项作业时,您将该作业标记为正在运行,这样就不会意外地再次拾取它。完成这项工作,写下结果并将其标记为完成。全部在一次交易中。当您的进程中途退出时,数据库将重置所有相关数据。
  • 在单独的连接和单独的事务中将作业进度写入单独的表行。浏览器可以轮询服务器以获取进度信息。您也可以使用 SignalR,但我没有这方面的经验,我预计在任意终止的情况下很难让它恢复进度报告。
  • 取消将通过在进度信息行中设置取消标志来完成。应用需要轮询该标志。

也许您可以使用消息队列来处理作业,但我总是很谨慎地使用它。要以事务方式处理消息,您需要 MSDTC,许多 SQL Server 的高可用性解决方案都不支持它。

您可能认为这种架构不是很复杂。它利用轮询来处理很多事情。轮询是一种原始技术,但效果很好。它可靠且易于理解。它有一个简单的并发模型。

如果您可以假设您的应用程序永远不会在不合时宜的时间退出,那么架构会简单得多。但这不能假设。您不能假设在工作时间不会进行部署,也不会假设没有导致崩溃的错误。

【讨论】:

  • 很好的答案,谢谢。我也在看windows azure,看到了一些类似角色工作者的东西,想知道它是否与此有关?我可以给角色工作者一个任务并要求它向数据库报告进度吗?这是它的目的吗?如果是这样的话,在经典的 Windows 服务器架构中是否有类似的东西?
  • 您几乎可以将“工作消费者”放置在任何地方。 Azure 工作人员非常适合消耗工作。您还可以将 Azure 队列作为激活作业处理的一种方式。在任何情况下,您都需要自己在数据库中跟踪进度和取消。在对 Windows Server 进行分类时,您可以让 Windows 服务运行您的代码。但这并不比使用 ASP.NET 站点更好。由于错误、重新启动或部署,它仍然可以随时退出。部署变得更加困难。没有服务的 xcopy 部署。需要先关机。
  • 很遗憾,您必须自己解决难题。只有激活部分可以推送到 Azure 队列或类似的队列中。崩溃一致性、进度和取消由您解决。
【解决方案2】:

即使使用 http worker 对运行长任务来说是一件坏事,我也做了一个小例子来说明如何使用 SignalR 管理它:

在这个例子中你可以:

  • 开始任务
  • 查看任务进度
  • 取消任务

它基于:

  • 推特引导
  • knockoutjs
  • 信号R
  • C# 5.0 async/await 与 CancelToken 和 IProgress

你可以在这里找到这个例子的来源:

https://github.com/dragouf/SignalR.Progress

【讨论】:

  • 这里有很棒的演示。是否可以仅使用 ajax 请求而不是 SignalR 来实现进度和取消?
  • 对不起 Adel Sal 这个演示的目的是使用 SignalR。但我想你应该能够找到使用 ajax 调用的示例。基本上,使用 ajax,您应该使用 javascript setTimeout 函数定期轮询服务器。服务器应该通过使用例如 asp.net 缓存来跟踪特定任务,就像在这篇 MSDN 文章中一样:msdn.microsoft.com/en-us/magazine/hh580729.aspx
  • 谢谢。这真的很有帮助:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-09
  • 1970-01-01
  • 2016-05-08
  • 2021-05-20
  • 1970-01-01
相关资源
最近更新 更多