2

我知道有很多 API 可以做到这一点,但我也知道托管环境(即 ASP.NET)限制了您可以在单独的线程中可靠地执行的操作。

我可能完全错了,所以如果我是,请纠正我,但这就是我认为我知道的。

  • 一个请求通常会在 120 秒后超时(这是可配置的),但最终 ASP.NET 运行时会终止一个需要很长时间才能完成的请求。
  • 托管环境(通常是 IIS)采用进程回收,并且可以随时决定回收您的应用程序。当这种情况发生时,所有线程都被中止并且应用程序重新启动。但是我不确定它有多激进,假设它会中止正常的正在进行的 HTTP 请求会有点愚蠢,但我希望它会中止一个线程,因为它对工作单元一无所知一个线程。

如果您必须创建一个编程模型,该模型可以轻松可靠地在理论上放置一个长时间运行的任务,并且必须运行数天,您将如何在 ASP.NET 应用程序中完成此任务?

以下是我对这个问题的看法:

我一直在考虑在 win32 服务中托管 WCF 服务。并通过 WCF 与服务对话。然而,这不是很实用,因为我选择这样做的唯一原因是从几个不同的 Web 应用程序发送任务(工作单元)。然后,我最终会要求服务提供状态更新并采取相应的行动。我对此最大的担忧是,如果我必须将每个任务部署到服务以使其能够执行一些指令,那将不是一个特别好的体验。还有这个输入问题,如果我有一个大数据集并且需要仔细研究它,我将如何为这个服务提供数据?

我现在通常做的是这个

SELECT TOP 10 * 
FROM WorkItem WITH (ROWLOCK, UPDLOCK, READPAST)
WHERE WorkCompleted IS NULL

它允许我将 SQL Server 数据库用作工作队列,并使用此查询定期轮询数据库以进行工作。如果工作项目成功完成,我将其标记为已完成并继续进行,直到没有更多事情可做。我不喜欢的是理论上我可以在任何时候被打断,如果我介于成功和将其标记为完成之间,我最终可能会处理两次相同的工作项目。我可能有点偏执,这可能一切都很好,但据我所知,不能保证不会发生这种情况......

我知道以前在 SO 上也有过类似的问题,但没有给出明确的答案。这是很常见的事情,但 ASP.NET 托管环境无法处理长时间运行的工作。

请分享你的想法。

4

4 回答 4

5

看看NServiceBus

NServiceBus 是一个用于 .NET 的开源通信框架,内置支持发布/订阅和长时间运行的进程。

它是一种基于 MSMQ 的技术,这意味着您的消息不会丢失,因为它们被持久化到磁盘上。然而,该框架具有令人印象深刻的性能和直观的 API。

于 2010-03-25T22:20:43.823 回答
3

约翰,

我同意 ASP.NET 不适合您所描述的异步任务,也不应该。它被设计为一个网络托管平台,而不是一个后台处理器。

我们过去也遇到过类似的情况,我们使用了与您描述的类似的解决方案。总之,将您的 WCF 服务保留在 ASP.NET 下,使用带有 Windows 服务的“Queue”表作为“QueueProcessor”。客户端应该轮询以查看工作是否完成(或使用消息传递来通知客户端)。

我们使用了一个包含流程及其信息的表(例如 InvoicingRun)。那张桌子上有一个状态(待定、正在运行、已完成、失败)。客户端将提交一个状态为 Pending 的新 InvoicingRun。Windows 服务(处理器)将轮询数据库以获取处于挂起阶段的任何运行(您也可以使用 SQL 通知,因此您不需要轮询。如果找到挂起的运行,它会将其移至运行,进行处理,然后将其移至完成/失败。

在进程发生致命故障的情况下(例如 DB 关闭、进程终止),运行将处于运行状态,需要人工干预。如果进程在非致命状态(异常、错误)下失败,则进程将移至失败,您可以选择重试或人工干预。

如果有多个处理器,第一个将其移动到运行状态的处理器就可以完成这项工作。您可以使用此方法来防止作业运行两次。替代方法是先选择然后更新以在事务下运行。确保其中任何一个在事务之外的更大的事务。示例(粗略)SQL:

UPDATE InvoicingRun
SET Status = 2 -- Running
WHERE ID = 1
    AND Status = 1 -- Pending

IF @@RowCount = 0
    SELECT Cast(0 as bit)
ELSE
    SELECT Cast(1 as bit)

于 2010-03-25T22:18:18.820 回答
0

是否考虑过使用 Workflow Foundation 而不是您的自定义实现?它还允许您持久化状态。在这种情况下,任务可以定义为工作流。

只是一些想法...

迈克尔

于 2010-03-25T22:07:43.550 回答
0

使用像Hangfire这样的简单后台任务/作业框架,并将这些最佳实践原则应用于解决方案的其余部分的设计:

  • 使所有动作尽可能小;为了达到这个目的,你应该——
  • 将长时间运行的作业分成批次并将它们排队(在 Hangfire 队列中或在其他类型的总线上)
  • 确保您的小作业(长作业的批处理部分)是幂等的(具有以任何顺序运行所需的所有上下文)。这样您就不必使用保持顺序的 quete;因为那样你就可以
  • 根据您的网络服务器场中有多少节点,并行执行队列中的作业。您甚至可以控制您的农场承受的负载量(作为服务 Web 请求的权衡)。这可确保您尽可能快速高效地完成整个作业(所有批次),同时不会影响您的集群为 Web 客户端提供服务。
于 2017-01-19T06:10:57.727 回答