【问题标题】:Long-running ASP.NET tasks长时间运行的 ASP.NET 任务
【发布时间】:2010-03-25 22:04:00
【问题描述】:

我知道有很多 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 托管环境无法处理长时间运行的工作。

请分享你的想法。

【问题讨论】:

    标签: asp.net asynchronous long-running-processes


    【解决方案1】:

    看看NServiceBus

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

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

    【讨论】:

    • 我没有这样做,但还不如这样做,因为它有点跑题了。 NServiceBus 似乎是一个用于构建分布式应用程序的消息传递框架。它与长时间运行的进程没有任何关系。它可能用于传输数据(发送消息),但它并没有真正说明同时依赖行为/指令,因此它有点失去它的意义。 NServiceBus 解决了哪些 WCF 没有解决的问题?
    • 它通过 SAGAS 支持长时间运行的工作流:nservicebus.com/Sagas.aspx。您不能使用 WCF 开箱即用
    • NServiceBus 内置了用于实现长时间运行页面的 ASP.NET 异步页面任务的集成,并且在即将到来的版本 (2.1) 中也支持 MVC AsyncController 集成。 NServiceBus 非常支持长时间运行的进程,以及以可靠和容错的方式促进与这些进程的通信。虽然您可以配置 WCF 来执行此操作,但您需要了解很多有关 WCF 的知识才能正确使用它,而使用 NServiceBus 时,默认情况下一切都以这种方式工作。 WCF 没有提供的另一件事是 MSMQ 的可靠负载平衡 - NServiceBus 提供。
    【解决方案2】:

    约翰,

    我同意 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)
    

    罗伯

    【讨论】:

    • 当你提到它时,这有点明显,没有想到这一点。我当然可以在开始处理后立即将该行标记为待处理,但在我进行任何实际工作之前。但是,如果例如,您如何防止一行陷入待处理状态?数据库在执行过程中下降? (这不太可能,但我想彻底)
    • 好吧,它可能会陷入“运行”状态。我认为一旦导致故障的问题得到解决,您需要让人工管理员将其移回(通过界面或直接在数据库中)。这是因为很难判断它是在运行还是失败如果这不是一个选项,请添加一个“看门狗”,在一定时间后重置它(当您确定进程失败时)。很难确定那个时间应该是什么时候。最后一种选择是在该行上保留一个事务,但随后会遇到并发访问问题。
    • 约翰,这是你一想到就很明显的事情之一。描述的方法是Mutex并发。
    • 我们两种 SQL 方法的组合应该可以解决任何并发问题,您可能需要定期检查服务是否按预期运行。
    • 关于 SQL 通知需要注意的是它已从 SQL Server 2008 中删除。此外,表后面的 IO 结构使它们不适合实现队列——这与聚集索引的行为有关主键导致数据库在“队列”扩展和收缩时重组数据布局。
    【解决方案3】:

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

    只是一些想法......

    迈克尔

    【讨论】:

    • 我没有,WWF 似乎是解决其他类型的异步业务导向任务的大型企业集团。这实际上只是在不同的线程中处理数字,但这样做是可靠的。不过我很欣赏这个建议。
    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 2012-08-13
      • 2012-06-08
      • 1970-01-01
      • 2017-12-14
      • 1970-01-01
      • 2022-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多