87

在 ASP.Net 应用程序中,用户单击网页上的按钮,然后通过事件处理程序实例化服务器上​​的对象并调用对象上的方法。该方法转到外部系统来做一些事情,这可能需要一段时间。所以,我想做的是在另一个线程中运行该方法调用,这样我就可以通过“您的请求已提交”将控制权返回给用户。我很高兴将其作为即发即弃的方式进行,尽管如果用户可以继续轮询对象的状态会更好。

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

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

4

10 回答 10

65

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

除了减少 IIS/ASP.NET 可用于处理请求的资源之外,当 AppDomain 被回收时,您还会遇到线程被杀死的问题,然后您必须在任务运行时处理任务的持久性,如以及在 AppDomain 恢复时开始备份工作。

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

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

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

编辑:以下是这种技术的一些实际应用示例:

社区服务器:使用 Windows 服务与后台线程以计划的时间间隔运行代码在 网站首次启动时创建后台线程

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

于 2009-02-11T21:53:01.407 回答
42

我不同意接受的答案。

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

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

(正如杰拉德在他的评论中指出的那样,现在还HostingEnvironment.QueueBackgroundWorkItem需要RegisterObject为后台项目注册一个调度程序来处理。总的来说,新方法更好,因为它是基于任务的。)

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

  • 使用 Web 部署不再进行琐碎的部署
  • 不能纯粹部署在 Azure 网站上
  • 根据后台任务的性质,进程可能必须进行通信。这意味着某种形式的 IPC 或服务必须访问一个公共数据库。

另请注意,某些高级方案甚至可能需要后台线程与请求在同一地址空间中运行。我认为 ASP.NET 可以做到这一点是通过 .NET 成为可能的巨大优势。

于 2014-09-15T08:43:44.903 回答
7

您不希望将 IIS 线程池中的线程用于此任务,因为它会使该线程无法处理未来的请求。您可以查看ASP.NET 2.0 中的异步页面,但这也不是正确的答案。相反,听起来您会从中受益的是研究Microsoft Message Queuing。本质上,您会将任务详细信息添加到队列中,而另一个后台进程(可能是 Windows 服务)将负责执行该任务。但底线是后台进程与 IIS 完全隔离。

于 2009-02-11T16:58:17.077 回答
7

我建议使用HangFire来满足这些要求。它是一个很好的后台运行的“一劳永逸”引擎,支持不同的架构,可靠,因为它由持久性存储支持。

于 2016-02-04T12:40:16.480 回答
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;
    }
于 2011-01-12T20:05:12.790 回答
5

我们从这条路开始,当我们的应用程序在一台服务器上时,它实际上运行良好。当我们想要扩展到多台机器(或在 Web garen 中使用多个 w3wp)时,我们必须重新评估并研究如何管理工作队列、错误处理、重试以及正确锁定以确保只有一个的棘手问题服务器拿起下一个项目。

...我们意识到我们不从事编写后台处理引擎的业务,因此我们寻找现有的解决方案,并使用了很棒的 OSS 项目hangfire

Sergey Odinokov 创建了一个真正的 gem,它非常容易上手,并允许您更换工作如何持久化和排队的后端。Hangfire 使用后台线程,但保留作业、处理重试并让您查看工作队列。因此,hangfire 作业是强大的,并且可以在所有变幻莫测的 appdomains 被回收等情况下幸存下来。

它的基本设置使用 sql server 作为存储,但您可以在需要扩展时换成 Redis 或 MSMQ。它还具有出色的 UI,用于可视化所有作业及其状态,并允许您重新排队作业。

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

有关可用选项的更多观点,请查看 Scott Hanselman 的博客,其中涵盖了在 asp.net 中处理后台作业的一些选项。(他给了hangfire一个热情洋溢的评论)

同样正如 John 所引用的,值得阅读 Phil Haack 的博客,了解为什么该方法存在问题,以及如何在卸载 appdomain 时优雅地停止线程上的工作。

于 2014-11-06T03:11:51.237 回答
2

您可以创建一个 Windows 服务来完成该任务吗?然后使用 .NET remoting 从 Web Server 调用 Windows Service 来执行操作?如果是这样的话,我会这样做。

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

如果不是,那么我会在该过程完成时强迫用户坐在那里。这样,您可以确保它已完成并且不会被 IIS 杀死。

于 2009-02-11T21:57:18.493 回答
2

似乎有一种受支持的方式可以在 IIS 中托管长期运行的工作。 工作流服务似乎是为此而设计的,尤其是与Windows Server AppFabric结合使用。该设计通过支持自动持久化和恢复长期运行的工作来允许应用程序池回收。

于 2012-10-05T15:19:18.757 回答
2

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

于 2016-10-27T03:21:52.937 回答
1

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

于 2009-02-13T13:21:55.050 回答