ASP webapp中的后台任务

时间:2011-06-20 23:32:55

标签: c# .net web-applications background

我是C#的新手,最近使用.NET 4.0构建了一个小型webapp。这个应用程序有两个部分:一个设计为永久运行,并将不断从Web上的给定资源获取数据。另一个根据请求访问该数据以进行分析。我正在努力完成第一部分。

我最初的方法是设置一个Timer对象来执行一个提取操作(无论那个操作在这里真的没什么关系),比如5分钟。我会在Application_Start上定义该计时器并让它在此之后生效。

但是,我最近意识到应用程序是根据用户请求创建/销毁的(根据我的观察,它们似乎在一段时间不活动后被破坏)。因此,我的后台活动将停止/恢复我的控制,我希望它能够连续运行,绝对没有中断。

所以这是我的问题:是否可以在网络应用中实现?或者我绝对需要一个单独的Windows服务来处理这类事情?

提前感谢您的宝贵帮助!

纪尧姆

7 个答案:

答案 0 :(得分:2)

虽然在网络应用上执行此操作并不理想.. 可以实现,因为该网站始终处于运行状态。

以下是一个示例:我在global.asax中创建了一个带有过期的Cache项。当它到期时,会触发一个事件。您可以在OnRemove()事件中获取数据或其他任何内容。

然后你可以设置一个页面调用(最好是一个非常小的页面),它将触发Application_BeginRequest中的代码,该代码会将Cache项目过期加回。

global.asax中:

private const string VendorNotificationCacheKey = "VendorNotification";
private const int IntervalInMinutes = 60; //Expires after X minutes & runs tasks

protected void Application_Start(object sender, EventArgs e)
{

   //Set value in cache with expiration time
   CacheItemRemovedCallback callback = OnRemove;

   Context.Cache.Add(VendorNotificationCacheKey, DateTime.Now, null, DateTime.Now.AddMinutes(IntervalInMinutes), TimeSpan.Zero,
                    CacheItemPriority.Normal, callback);
}

private void OnRemove(string key, object value, CacheItemRemovedReason reason)
{
    SendVendorNotification();

    //Need Access to HTTPContext so cache can be re-added, so let's call a page. Application_BeginRequest will re-add the cache.
    var siteUrl = ConfigurationManager.AppSettings.Get("SiteUrl");
    var client = new WebClient();
    client.DownloadData(siteUrl + "default.aspx");
    client.Dispose();

}

private void SendVendorNotification()
{
    //Do Tasks here
}

protected void Application_BeginRequest(object sender, EventArgs e)
{
    //Re-add if it doesn't exist
    if (HttpContext.Current.Request.Url.ToString().ToLower().Contains("default.aspx") &&
        HttpContext.Current.Cache[VendorNotificationCacheKey] == null)
    {
        //ReAdd
        CacheItemRemovedCallback callback = OnRemove;
        Context.Cache.Add(VendorNotificationCacheKey, DateTime.Now, null, DateTime.Now.AddMinutes(IntervalInMinutes), TimeSpan.Zero,
                          CacheItemPriority.Normal, callback);
    }
}

如果您的预定任务很快,这很有效。 如果这是一个漫长的过程......你肯定需要将它从你的网络应用程序中删除。

只要第一个请求启动了应用程序......即使网站上没有访问者,也会每60分钟继续点击一次。

答案 1 :(得分:2)

我建议把它放在Windows服务中。你避免了上面提到的所有箍,最重要的是IIS重启。 Windows服务还具有以下优点:

  1. 服务器启动时可以自动启动。如果您在IIS中运行并且服务器重新启动,则必须等到请求启动您的进程。
  2. 如果需要,可以将此数据提取过程放在另一台计算机上
  3. 如果您最终在多台服务器上对您的网站进行负载均衡,您可能会意外地有多个数据提取过程导致您出现问题
  4. 更容易单独编写代码(单一责任原则)。如果代码正在执行它需要做的事情而不是试图欺骗IIS,则更容易维护代码。

答案 2 :(得分:1)

使用构造函数创建静态类,创建计时器事件。 不过像史蒂夫·斯洛卡(Steve Sloka)所提到的那样,IIS有一个超时时间你必须操纵以保持网站运行。

using System.Runtime.Remoting.Messaging;

public static class Variables
{
static Variables()
{
    m_wClass = new WorkerClass();

    // creates and registers an event timer
    m_flushTimer = new System.Timers.Timer(1000);
    m_flushTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnFlushTimer);
    m_flushTimer.Start();
}

private static void OnFlushTimer(object o, System.Timers.ElapsedEventArgs args)
{
    // determine the frequency of your update
    if (System.DateTime.Now - m_timer1LastUpdateTime > new System.TimeSpan(0,1,0))
    {
        // call your class to do the update
        m_wClass.DoMyThing();
        m_timer1LastUpdateTime = System.DateTime.Now;
    }
}

private static readonly System.Timers.Timer m_flushTimer;
private static System.DateTime m_timer1LastUpdateTime = System.DateTime.MinValue;
private static readonly WorkerClass m_wClass;
}

public class WorkerClass
{
public delegate WorkerClass MyDelegate();

public void DoMyThing()
{
    m_test = "Hi";
    m_test2 = "Bye";
    //create async call to do the work
    MyDelegate myDel = new MyDelegate(Execute);
    AsyncCallback cb = new AsyncCallback(CommandCallBack);
    IAsyncResult ar = myDel.BeginInvoke(cb, null);
}

private WorkerClass Execute()
{
    //do my stuff in an async call
    m_test2 = "Later";
    return this;
}

public void CommandCallBack(IAsyncResult ar)
{
    // this is called when your task is complete
    AsyncResult asyncResult = (AsyncResult)ar;
    MyDelegate myDel = (MyDelegate)asyncResult.AsyncDelegate;
    WorkerClass command = myDel.EndInvoke(ar);

    // command is a reference to the original class that envoked the async call
    // m_test will equal "Hi"
    // m_test2 will equal "Later";
}

private string m_test;
private string m_test2;
}

答案 3 :(得分:0)

我认为您可以使用BackgroundWorker来实现它,但我建议您去寻求服务。

答案 4 :(得分:0)

只要IIS中的工作进程正常运行,您的应用程序上下文就会存在。在IIS中,工作进程何时回收会有一些默认超时(例如,空闲分钟数(20)或定期间隔(1740)。

也就是说,如果您在IIS中调整这些设置,您应该可以使用这些请求,但是,使用服务的其他答案也可以正常工作,这只是您想要实现的方式。

答案 5 :(得分:0)

我最近制作了一个文件上传功能,用于将Access文件上传到数据库(不是最好的方法,只是对长期问题的临时修复)。 我通过创建一个贯穿ProcessAccess函数的后台线程来解决它,并在完成时被删除。

除非IIS有一个设置,它会在一段时间内杀死一个线程而不管是不活动的,你应该能够创建一个调用永不结束的函数的线程。不要使用递归,因为开放函数的数量最终会在你面前爆炸,但只有for(;;)循环5,000,000次,所以它会保持忙碌:)

答案 6 :(得分:0)

IIS 7.5的应用程序初始化模块正是这种类型的init工作。有关该模块的更多详细信息,请访问Application Initialization Module