我有一个ASP.NET MVC3应用程序,可以处理耗时的进程(从网络复制大文件)。我们想做的是:
我们的想法是,用户不需要对流程进度进行任何确认,也不会在流程完成时收到通知。
我们目前让控制器在Windows服务中触发事件,并使用Windows服务执行实际工作。我想知道是否有更好/更清洁的方法来做到这一点?
答案 0 :(得分:6)
您可以System.Threading.Tasks.Task使用StartNew调用Action delegate方法。
使用这些工具,你的控制器看起来像这样:
[HttpPost]
public ActionResult DoSomethingLongRunning()
{
if (ModelState.IsValid)
{
Task.Factory.StartNew(() =>
fileCopier.CopyFile(CopyFileParameter1, CopyFileParameter2));
return RedirectToAction("View Indicating Long Running Progress");
}
else
{
// there is something wrong with the Post, handle it
return View("Post fallback view");
}
}
另一种选择是你可以使用System.Reactive.Concurrency和IScheduler接口与TaskPoolScheduler作为执行操作的具体实现(可能在控制器构造函数中注入。
public ActionResult DoSomethingLongRunning()
{
if (ModelState.IsValid)
{
ISchedulerImplementation.Schedule(new Action(() =>
{
fileCopier.CopyFile(CopyFileParameter1, CopyFileParameter2);
}));
return RedirectToAction("View Indicating Long Running Progress");
}
else
{
// there is something wrong with the Post, handle it
return View("Post fallback view");
}
}
作为一个好处,如果你这样做,你可以在单元测试时使用TestScheduler作为接口的实现。
答案 1 :(得分:3)
我认为让Windows服务主机成为您长期运行的过程是最好的选择。如果它是由IIS托管的,那么你总是冒着它正在运行的应用程序池的风险因为不活动而被杀死。
一种相关的可能性是在Windows服务中托管WCF服务,并为该服务提供外部HTTP或其他端点。这样,您的Web界面可以调用WCF服务的“开始”合同方法,如果需要,还可以调用其他方法。
答案 2 :(得分:2)
我将向IIS 7 WAS中托管的WCF MSMQ服务发布请求。有an awesome article如何设置它。
使用外部资源的长时间运行任务具有很高的失败风险。开发人员经常犯的最大错误是假设硬件和网络具有无限容量并且非常可靠。通常不是。
如果长时间运行的进程因暂时失去网络连接而中断,或者远程服务器正在重新启动,则可能会出现问题,甚至是灾难性的。如果长时间运行的进程包括其他处理,例如解压缩文件或进行分析,则如果没有足够的内存来执行处理,则可能会进一步发生故障。用户可能会提交太多请求,并且没有足够的资源来处理并发问题。如果让ASP.NET MVC应用程序通过async controllers进行处理,那么当IIS回收工作进程时,当您长时间运行的进程被中断时,您可能会感到惊讶。
MSMQ 4在缓解这些风险方面做得相当不错。如果该过程失败,您可以在放弃之前重试几次。您可以了解如何设置here。您可以使用application specific deadletter queues来处理在多次可接受尝试后进程失败的情况。这对于操作人员诊断问题很重要。您也可以使用此方案通过电子邮件通知用户该过程失败(或成功),即使请求源自的机器已关闭。
在IIS而不是Windows服务中托管它提供了额外的功能。例如,IIS工作进程可以在其死锁或超过内存阈值时进行回收。当您使用某些本机代码执行处理时,后者可能是一个问题。您可以每四小时(选择您的时间表)小时回收它。后者在处理大量托管内存时非常重要,因为随着时间的推移,大对象头部碎片化得太多,以至于几乎无法管理为另一个大型请求分配足够的内存。您可能会发现Windows服务中托管的WCF服务可能会遇到此问题。
实际上,这取决于您希望后台进程的可靠性。如果没有,使用WCF,MSMQ和IIS可能只是过度杀伤。