我可以使用线程在IIS上执行长时间运行的作业吗?

时间:2009-02-11 13:09:59

标签: asp.net multithreading iis background-process

在ASP.Net应用程序中,用户单击网页上的按钮,然后通过事件处理程序在服务器上实例化对象,并调用该对象上的方法。 该方法转到外部系统来做东西,这可能需要一段时间。所以,我想要做的是在另一个线程中运行该方法调用,以便我可以通过“您的请求已经提交”将控制权返回给用户。 我很乐意这样做,虽然如果用户可以继续轮询对象的状态,那就更好了。

我不知道的是,即使用户会话过期,IIS是否允许我的线程继续运行。 想象一下,用户触发事件并在服务器上实例化对象并在新线程中触发该方法。用户对“您的请求已提交”消息感到满意并关闭其浏览器。最终,此用户会话将在IIS上超时,但该线程可能仍在运行,正在运行。 IIS会允许线程继续运行还是会在用户会话到期后将其终止并处理掉对象?

编辑:从答案和评论中,我了解到最好的方法是将长时间运行的处理移到IIS之外。除了其他一切,这涉及appdomain回收问题。在实践中,我需要在有限的时间内完成版本1并且必须在现有框架内工作,因此希望避免服务层,因此希望在IIS内部启动线程。实际上,这里的“长时间运行”只需几分钟,网站上的并发性很低,所以应该没问题。但是,下一个版本肯定需要拆分成一个单独的服务层。

10 个答案:

答案 0 :(得分:63)

你可以完成你想要的,但这通常是一个坏主意。一些ASP.NET博客和CMS引擎采用这种方法,因为它们希望可以安装在共享主机系统上,而不依赖于需要安装的Windows服务。通常,当应用程序启动时,它们会在Global.asax中启动一个长时间运行的线程,并让该线程进程排队等待任务。

除了减少IIS / ASP.NET可用于处理请求的资源之外,当AppDomain被回收时,您还遇到了线程被杀死的问题,然后您必须处理任务的持久性。当AppDomain重新启动时,以及重新启动工作。

请记住,在许多情况下,AppDomain会以默认时间间隔自动回收,以及更新web.config等时。

如果您可以随时处理线程的持久性和事务性方面的处理,那么您可以通过让某些外部进程在某个时间间隔内对您的站点发出请求来绕过AppDomain回收 - 这样如果该站点回收后,您可以保证在X分钟内自动重新启动。

同样,这通常是一个坏主意。

编辑:以下是这项技术的一些实例:

Community Server: Using Windows Services vs. Background Thread to Run Code at Scheduled Intervals Creating a Background Thread When Website First Starts

编辑(来自遥远的未来) - 这些天我会使用Hangfire

答案 1 :(得分:32)

我不同意接受的答案。

在ASP.NET中使用后台线程(或以Task.Factory.StartNew开头的任务)。与所有托管环境一样,您可能希望了解并配合管理关闭的设施。

在ASP.NET中,您可以使用HostingEnvironment.RegisterObject方法注册需要在关机时正常停止的工作。请参阅this article以及讨论的评论。

(正如Gerard在他的评论中指出的那样,现在还有HostingEnvironment.QueueBackgroundWorkItem调用RegisterObject来注册后台项目的调度程序。总的来说,新方法更好,因为它的任务基。)

至于您经常听到的一般主题是一个坏主意,请考虑部署Windows服务(或其他类型的额外流程应用程序)的替代方案:

  • 不再使用Web部署轻松部署
  • 不可完全部署在Azure网站上
  • 根据后台任务的性质,进程可能需要进行通信。这意味着要么某种形式的IPC,要么服务必须访问公共数据库。

另请注意,某些高级方案甚至可能需要后台线程在与请求相同的地址空间中运行。我看到ASP.NET可以通过.NET实现这一点,这是一个很好的优势。

答案 2 :(得分:7)

您不希望使用IIS线程池中的线程执行此任务,因为这会使该线程无法处理将来的请求。您可以查看Asynchronous Pages in ASP.NET 2.0,但这也不是正确的答案。相反,你会从中受益的是Microsoft Message Queuing。基本上,您可以将任务详细信息添加到队列中,另一个后台进程(可能是Windows服务)将负责执行该任务。但最重要的是后台进程与IIS完全隔离。

答案 3 :(得分:6)

我建议使用HangFire来满足此类要求。它是一个很好的火和忘记引擎在后台运行,支持不同的架构,可靠,因为它由持久性存储支持。

答案 4 :(得分:5)

这里有一个很好的主题和示例代码:http://forums.asp.net/t/1534903.aspx?PageIndex=2

我甚至玩弄了从线程中调用我的网站上的保持活动页面的想法,以帮助保持应用程序池活着。请记住,如果您使用此方法需要非常好的恢复处理,因为应用程序可以随时回收。正如许多人所说,如果您可以访问其他服务选项,这不是正确的方法,但对于共享主机,这可能是您唯一的选择之一。

为了帮助保持应用程序池的活跃,您可以在线程处理时向您自己的站点发出请求。如果您的流程运行很长时间,这可能有助于保持应用程序池的活跃。

string tempStr = GetUrlPageSource("http://www.mysite.com/keepalive.aspx");


    public static string GetUrlPageSource(string url)
    {
        string returnString = "";

        try
        {
            Uri uri = new Uri(url);
            if (uri.Scheme == Uri.UriSchemeHttp)
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
                CookieContainer cookieJar = new CookieContainer();

                req.CookieContainer = cookieJar;

                //set the request timeout to 60 seconds
                req.Timeout = 60000;
                req.UserAgent = "MyAgent";

                //we do not want to request a persistent connection
                req.KeepAlive = false;

                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                Stream stream = resp.GetResponseStream();
                StreamReader sr = new StreamReader(stream);

                returnString = sr.ReadToEnd();

                sr.Close();
                stream.Close();
                resp.Close();
            }
        }
        catch
        {
            returnString = "";
        }

        return returnString;
    }

答案 5 :(得分:4)

我们开始沿着这条路走下去,当我们的应用程序在一台服务器上时,它确实工作正常。当我们想要扩展到多台机器(或在web garen中使用多个w3wp)时,我们不得不重新评估并查看如何管理工作队列,错误处理,重试以及正确锁定的棘手问题以确保只有一个服务器选择下一个项目。

......我们意识到我们不是在编写后台处理引擎的业务,因此我们寻找现有的解决方案,并使用了令人敬畏的OSS项目hangfire

Sergey Odinokov已经创建了一个非常容易上手的真正的宝石,并允许你换掉工作持久和排队的后端。 Hangfire使用后台线程,但会保留作业,处理重试并让您可以看到工作队列。因此,hangfire工作非常强大,并且可以在所有应用程序域的变幻无常的环境中生存下来。

它的基本设置使用sql server作为存储,但是当它可以扩展时,你可以换掉Redis或MSMQ。它还具有出色的UI,可以显示所有作业及其状态,并允许您重新排队作业。

我的观点是,虽然完全有可能在后台线程中做你想做的事,但还有很多工作可以使它具有可扩展性和健壮性。它适用于简单的工作负载,但是当事情变得更复杂时,我更喜欢使用专门构建的库而不是通过这种努力。

有关可用选项的更多视角,请查看Scott Hanselman的blog,其中介绍了在asp.net中处理后台作业的几个选项。 (他给了篝火一个热烈的评论)

同样如John所述,它值得阅读Phil Haack的blog,了解该方法存在问题的原因,以及如何在卸载appdomain时优雅地停止线程上的工作。

答案 6 :(得分:2)

您可以创建一个Windows服务来执行该任务吗?然后使用Web服务器的.NET远程控制来调用Windows服务来执行操作?如果是这种情况就是我要做的。

这将消除在IIS上进行中继的需要,并限制其部分处理能力。

如果没有,那么我会强制用户在完成该过程时坐在那里。这样你就可以确保它完成而不会被IIS杀死。

答案 7 :(得分:2)

似乎有一种支持的方式在IIS中托管长时间运行的工作。 Workflow Services似乎是为此设计的,尤其是与Windows Server AppFabric一起使用。该设计允许通过支持自动持久性和恢复长期工作来回收应用程序池。

答案 8 :(得分:2)

您可以在后台运行任务,即使请求结束,它们也会完成。 不要抛出未捕获的异常。通常,您希望始终抛出异常。如果在新线程中抛出异常,那么它将使IIS worker process - w3wp.exe崩溃,因为您不再处于请求的上下文中。如果您正在使用它们,那么除了进程内存备份会话之外,还会杀死您运行的任何其他后台任务。这很难诊断,这就是为什么不鼓励这种做法。

答案 9 :(得分:1)

只需创建代理进程即可运行异步任务;它不一定是一个Windows服务(尽管在大多数情况下这是更优化的方法.MSMQ已经过时了。