为ASP.NET设计异步任务库

时间:2010-10-06 05:48:17

标签: asp.net

ASP.NET运行时用于可以并行运行的短工作负载。我需要能够安排可能会或可能不会运行更长时间的定期事件和后台任务。

鉴于上述情况,我有以下问题需要处理:

  • AppDomain可能因更改而关闭(Web.config,bin,App_Code等)
  • IIS定期(每天)回收AppPool
  • IIS本身可能会重新启动,或者服务器可能会崩溃

我不相信在ASP.NET中运行此代码不是正确的做法,因为它将允许更简单的编程模型。但这样做会要求外部服务定期向应用程序发出请求,以便应用程序保持运行状态,并且所有后台任务都经过精心编程。如果发生意外错误,他们必须能够暂停和恢复他们的工作。

我目前的思路是这样的:

如果所有作业都在数据库中注册,则应该可以将数据库用作簿记机制。在出现错误的情况下,数据库将包含在下次给出的机会下恢复操作所需的所有状态。

在这件事情上,我真的会提出一些反馈/意见。我一直在考虑运行Windows服务并使用一些RPC解决方案,但它对我没有同样的吸引力。而且我会遇到很多部署问题,并且会将任务和代码交叉到多个应用程序中。由于我的业务需求,这不是最好的。

4 个答案:

答案 0 :(得分:4)

由于我不知道你使用的是什么数据库,所以这是一个黑暗的镜头,但我建议你考虑dialog timersactivation。假设大多数的作业必须进行一些数据操作,并且很可能所有必须数据操作,利用激活和定时器提供一个非常可靠的作业调度解决方案,完全嵌入在数据库中(不需要外部进程/服务,不需要像msdb这样的数据库边界之外的依赖关系),并且是一种确保预定作业可以在重启,故障转移事件甚至灾难中生存的解决方案恢复恢复。简单地说,一旦安排了一个作业,即使数据库在一周后在另一台机器上恢复,它也会运行。

请查看Asynchronous procedure execution以获取相关示例。

如果这太激进了,至少要查看Using Tables as Queues,因为将预定项目存储在数据库中通常属于“待处理队列”的情况。

答案 1 :(得分:2)

我建议您查看Quartz.Net。它是开源的,它会给你一些想法。

答案 2 :(得分:2)

将数据库用作状态保持机制是一个完全有效的想法。它的复杂程度取决于你想要走多远。在许多情况下,您最终会将数据库逻辑与Windows服务配对以获得所需的结果。

FWIW,在ASP.Net应用程序中手动使用线程池通常不是一个好习惯,但是(与你可能读到的相反)它实际上工作得非常好,除了你不能<强>保证它会起作用。

因此,如果您需要一个后台线程,每隔30秒检查一个对象的状态,并且您不关心它是否每30秒或29秒或2分钟(例如在长应用程序池中循环), ASP.Net-spawned线程是一个快速而又非常糟糕的解决方案。

异步触发的回调(例如在ASP.Net Cache对象上)也可以执行一种“幕后”角色。

我遇到了类似的挑战,并最终选择了使用构建块组合的Windows服务,以实现最大的灵活性。即,我使用:

1)WCF具有特定于实现的类型OR

2)用于传输和管理包装作业的对象的类型或

3)自定义包装器中包含的完全通用的可序列化对象。由于它们只是二进制有效负载,因此允许将任何对象传递给服务。一旦进入服务,包装器就会定义对象应该发生什么(例如,调用方法,收集结果,并可选择使结果可用于返回)。

最终,该网站负责查询有关其状态的服务。这个查询可以像轮询一样简单,也可以使用WCF进行异步回调(虽然我相信这也会在幕后使用某种轮询)。

答案 3 :(得分:1)

我告诉你我做了什么。

我创建了一个名为 Atzenta 的类,它有一个计时器(1-2秒触发器)。 我还在我的临时数据库上创建了一个表来保存作业。该表知道jobID,其他参数,优先级,作业状态,消息。

我可以在此课程中添加或删除作业。当没有动作要做时,计时器停止。当我添加作业时,计时器再次启动。 (计时器是他自己的一个线程,可以做并行工作)。我使用System.Timers and not other timers for this

作业可以有不同的优先级。

现在让我说我使用 Atzenta 类在这张桌子上放置一份工作。下次定时器触发时检查此表上的查询并找到第一个可用作业并运行它。在此结束之前,没有其他工作可以运行。

每个同步和标志都是从表中完成的。在表中,我有每个作业的标志,显示它是否等待运行|请求运行|暂停|结束|终止|

所有作业都是现成的已知函数或类(例如,创建统计信息)。

对于停止和启动,我使用global.asax和Application_Start,Application_End来启动和暂停保留任务的对象。例如,当我做一份工作,然后我得到Application_End以太,我等待完成然后停止应用程序,以太停止操作,通知表,然后再次启动application_start。

所以我说, Atzenta.RunTheJob(Jobs.StatisticUpdate,ProductID);然后我在桌面上添加这个工作,打开计时器,然后在触发器上运行这个工作,我更新给定产品ID的统计数据。

我在数据库上使用一个表来同步运行相同Web应用程序的许多池,实际上它是这样工作的。使用公共表,可以轻松地同步作业,并且可以避免2个池同时运行相同的作业。

在我的后台办公室,我有一个简单的表视图,可以查看所有工作的状态。