Фоновая задача в веб-приложении ASP - PullRequest
6 голосов
/ 21 июня 2011

Я довольно новичок в C # и недавно создал небольшое веб-приложение, используя .NET 4.0.Это приложение состоит из 2 частей: одна предназначена для постоянной работы и будет постоянно извлекать данные из заданных ресурсов в Интернете.Другой обращается к этим данным по запросу для их анализа.Я борюсь с первой частью.

Мой первоначальный подход состоял в том, чтобы установить Timer объект, который будет выполнять операцию выборки (независимо от того, какая операция здесь не имеет значения) каждые, скажем, 5минут.Я бы определил этот таймер на Application_Start и позволил бы ему жить после этого.

Однако недавно я понял, что приложения создаются / уничтожаются на основе пользовательских запросов (из моего наблюдения они, похоже, разрушаются через некоторое времяпассивность).Как следствие, моя фоновая активность остановится / возобновится вне моего контроля, где я хотел бы, чтобы она работала непрерывно, без каких-либо перерывов.

Итак, вот мой вопрос : это достижимо?в веб-приложении?Или мне абсолютно необходим отдельный сервис Windows для такого рода вещей?

Заранее благодарим за вашу драгоценную помощь!

Гийом

Ответы [ 7 ]

2 голосов
/ 01 декабря 2011

Я предлагаю положить его в службу Windows. Вы избегаете всех упомянутых выше обручей, главным из которых является перезапуск IIS. Служба Windows также имеет следующие преимущества:

  1. Может автоматически запускаться при запуске сервера. Если вы работаете в IIS и ваш сервер перезагружается, вам нужно подождать, пока не будет сделан запрос на запуск вашего процесса.
  2. Может разместить этот процесс извлечения данных на другом компьютере, если необходимо.
  3. Если вы в конечном итоге распределите нагрузку на свой веб-сайт на нескольких серверах, вы можете случайно получить несколько процессов извлечения данных, что вызовет проблемы
  4. Проще вводить код отдельно (принцип единой ответственности). Проще поддерживать код, если он просто делает то, что ему нужно, и не пытается обмануть IIS.
2 голосов
/ 23 ноября 2011

Хотя делать это в веб-приложении не идеально. это достижимо , учитывая, что сайт всегда работает.

Вот пример: я создаю элемент Cache в global.asax с истечением срока действия. Когда он истекает, событие запускается. Вы можете получить свои данные или что-либо еще в событии 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);
    }
}

Это хорошо работает, если запланированное задание выполняется быстро. Если это длительный процесс ... вам определенно нужно не допускать его в свое веб-приложение.

Пока 1-й запрос запустил приложение ... оно будет срабатывать каждые 60 минут, даже если у него нет посетителей на сайте.

1 голос
/ 30 сентября 2011

Создание статического класса с помощью конструктора, создание события таймера.Однако, как упоминал Стив Слока, у 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;
}
0 голосов
/ 09 октября 2012

Модуль инициализации приложения для IIS 7.5 выполняет именно этот тип инициализации.Более подробная информация о модуле доступна здесь Модуль инициализации приложения

0 голосов
/ 22 ноября 2011

Я недавно сделал функцию загрузки файлов для загрузки файлов Access в базу данных (не лучший способ, а временное исправление долгосрочной проблемы). Я решил эту проблему, создав фоновый поток, который выполнялся через функцию ProcessAccess и был удален после завершения.

Если IIS не имеет настройки, в которой он убивает поток через заданный промежуток времени, независимо от бездействия, вы сможете создавать поток, который вызывает функцию, которая никогда не заканчивается. Не используйте рекурсию, потому что количество открытых функций в конечном итоге взорвется, но просто создайте цикл for (;;) 5 000 000 раз, чтобы он был занят:)

0 голосов
/ 21 сентября 2011

Контекст вашего приложения живет до тех пор, пока ваш рабочий процесс в IIS функционирует. В IIS есть некоторые тайм-ауты по умолчанию для того, когда рабочий процесс будет перезагружен (например, Количество минут простоя (20) или регулярные интервалы (1740).

Тем не менее, если вы отрегулируете эти настройки в IIS, вы сможете выполнять запросы в реальном времени, однако другие ответы об использовании службы также будут работать, просто вопрос того, как вы хотите реализовать.

0 голосов
/ 21 июня 2011

Я думаю, вы можете достичь этого, используя BackgroundWorker , но я бы скорее предложил вам обратиться за услугой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...