Могу ли я использовать потоки для выполнения длительных заданий в IIS?

StackOverflow https://stackoverflow.com/questions/536681

Вопрос

В ASP.Сетевое приложение пользователь нажимает кнопку на веб-странице, а затем создает экземпляр объекта на сервере с помощью обработчика событий и вызывает метод для объекта.Метод отправляется во внешнюю систему для выполнения каких-либо действий, и это может занять некоторое время.Итак, что я хотел бы сделать, так это запустить вызов этого метода в другом потоке, чтобы я мог вернуть управление пользователю с сообщением "Ваш запрос был отправлен".Я разумно рад сделать это как запустить и забыть, хотя было бы еще приятнее, если бы пользователь мог продолжать опрашивать объект на предмет статуса.

Чего я не знаю, так это того, позволяет ли IIS моему потоку продолжать работать, даже если срок действия пользовательского сеанса истекает.Представьте, пользователь запускает событие, а мы создаем экземпляр объекта на сервере и запускаем метод в новом потоке.Пользователь доволен сообщением "Ваш запрос отправлен" и закрывает свой браузер.В конце концов, время ожидания сеанса этого пользователя в IIS истечет, но поток все еще может быть запущен, выполняя работу.Позволит ли IIS потоку продолжать работать или он убьет его и удалит объект по истечении пользовательского сеанса?

Редактировать:Из ответов и комментариев я понимаю, что лучший способ сделать это - перенести длительную обработку за пределы IIS.Помимо всего прочего, это связано с проблемой утилизации домена приложения.На практике мне нужно запустить версию 1 с нуля за ограниченное время, и она должна работать внутри существующей платформы, поэтому хотелось бы избежать уровня сервиса, отсюда и желание просто запустить поток внутри IIS.На практике "длительный запуск" здесь займет всего несколько минут, а параллелизм на веб-сайте будет низким, так что все должно быть в порядке.Но в следующей версии определенно потребуется разделение на отдельный уровень обслуживания.

Это было полезно?

Решение

Вы можете добиться того, чего хотите, но, как правило, это плохая идея.Несколько ASP.NET движки блогов и CMS используют этот подход, потому что они хотят, чтобы их можно было устанавливать в системе общего хостинга и не зависеть от службы Windows, которую необходимо установить.Обычно они запускают длительный поток в Global.asax при запуске приложения и помещают задачи этого потока в очередь.

В дополнение к сокращению ресурсов, доступных IIS / ASP.NET для обработки запросов, у вас также возникают проблемы с завершением потока при повторном использовании домена приложения, и затем вам приходится иметь дело с сохранением задачи во время ее выполнения, а также с запуском работы при восстановлении домена приложения.

Имейте в виду, что во многих случаях домен приложения перерабатывается автоматически с интервалом по умолчанию, а также при обновлении web.config и т.д.

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

Опять же, как правило, это плохая идея.

Редактировать:Вот несколько примеров этой техники в действии:

Сервер сообщества:Использование служб Windows противФоновый поток для запуска кода с запланированными интервалами Создание фонового потока При первом запуске веб-сайта

РЕДАКТИРОВАТЬ (из далекого будущего) - В наши дни я бы использовал Похмелье.

Другие советы

Я не согласен с общепринятым ответом.

Использование фонового потока (или задачи, запущенной с Task.Factory.StartNew) прекрасно подходит в ASP.NET.Как и во всех средах размещения, вы можете захотеть разобраться в средствах, регулирующих завершение работы, и сотрудничать с ними.

В разделе ASP.NET вы можете зарегистрировать работу, которая должна быть корректно остановлена при завершении работы, используя HostingEnvironment.RegisterObject способ.Видишь эта статья и комментарии для обсуждения.

(Как указывает Джерард в своем комментарии, теперь также HostingEnvironment.QueueBackgroundWorkItem это призывает к RegisterObject зарегистрировать планировщик для фонового элемента для работы.В целом, новый метод приятнее, поскольку он основан на задачах.)

Что касается общей темы, которую вы часто слышите о том, что это плохая идея, рассмотрите альтернативу развертывания службы Windows (или другого вида приложения вне процесса):

  • Нет более тривиального развертывания с помощью web deploy
  • Не развертывается исключительно на веб-сайтах Azure
  • В зависимости от характера фоновой задачи процессам, скорее всего, придется взаимодействовать.Это означает, что либо какая-то форма IPC, либо службе придется обращаться к общей базе данных.

Обратите также внимание, что в некоторых продвинутых сценариях может даже потребоваться, чтобы фоновый поток выполнялся в том же адресном пространстве, что и запросы.Я рассматриваю тот факт, что ASP.NET могу это сделать, как большое преимущество, которое стало возможным благодаря .NET.

Вы бы не хотели использовать поток из пула потоков IIS для этой задачи, потому что это сделало бы этот поток неспособным обрабатывать будущие запросы.Вы могли бы заглянуть в Асинхронные страницы в ASP.NET 2.0, но это тоже действительно было бы неправильным ответом.Вместо этого, похоже, вам было бы полезно изучить Постановка сообщений Microsoft в очередь.По сути, вы бы добавили сведения о задаче в очередь, и другой фоновый процесс (возможно, служба Windows) отвечал бы за выполнение этой задачи.Но суть в том, что фоновый процесс полностью изолирован от IIS.

Я бы предложил использовать Похмелье для таких требований.Это хороший движок "включи и забудь", работающий в фоновом режиме, поддерживающий другую архитектуру, надежный, потому что он поддерживается постоянным хранилищем.

Здесь есть хорошая тема и пример кода: http://forums.asp.net/t/1534903.aspx ?Индекс страницы=2

Я даже поиграл с идеей вызвать страницу keep alive на моем веб-сайте из потока, чтобы помочь поддерживать пул приложений в рабочем состоянии.Имейте в виду, если вы используете этот метод, что вам нужна действительно хорошая обработка восстановления, потому что приложение может повторно использовать его в любое время.Как многие уже упоминали, это неправильный подход, если у вас есть доступ к другим вариантам обслуживания, но для общего хостинга это может быть одним из ваших единственных вариантов.

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

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;
    }

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

...мы поняли, что не занимаемся написанием механизмов фоновой обработки, поэтому мы искали существующие решения и остановились на потрясающем проекте OSS похмелье

Сергей Одиноков создал настоящую жемчужину, с которой действительно легко начать и которая позволяет вам поменять местами серверную часть того, как работа сохраняется и ставится в очередь.Hangfire использует фоновые потоки, но сохраняет задания, обрабатывает повторные попытки и предоставляет вам доступ к рабочей очереди.Таким образом, задания hangfire надежны и выдерживают все капризы переработки доменов приложений и т.д.

Его базовая настройка использует sql server в качестве хранилища, но вы можете заменить его на Redis или MSMQ, когда придет время масштабировать.Он также имеет отличный пользовательский интерфейс для визуализации всех заданий и их статуса, а также позволяет повторно ставить задания в очередь.

Я хочу сказать, что, хотя в фоновом потоке вполне возможно делать то, что вы хотите, предстоит проделать большую работу, чтобы сделать его масштабируемым и надежным.Это прекрасно для простых рабочих нагрузок, но когда все становится более сложным, я предпочитаю использовать специально созданную библиотеку, а не прибегать к таким усилиям.

Для получения более подробной информации о доступных вариантах ознакомьтесь с книгой Скотта Хансельмана Блог который охватывает несколько вариантов обработки фоновых заданий в asp.net.(Он дал hangfire восторженный отзыв)

Также, как упоминает Джон, стоит прочитать книгу Фила Хаака Блог о том, почему этот подход проблематичен и как изящно остановить работу над потоком при выгрузке appdomain.

Можете ли вы создать службу Windows для выполнения этой задачи?Затем используйте .СЕТЕВОЕ удаленное подключение с веб-сервера для вызова службы Windows для выполнения действия?Если это так, то именно это я бы и сделал.

Это устранило бы необходимость ретрансляции в IIS и ограничило бы часть его вычислительной мощности.

Если нет, то я бы заставил пользователя сидеть там, пока процесс не будет завершен.Таким образом, вы гарантируете, что он завершен и не уничтожен IIS.

Похоже, существует один поддерживаемый способ размещения длительной работы в IIS. Службы документооборота кажутся предназначенными для этого, особенно в сочетании с Windows Server AppFabric для Windows Server AppFabric.Конструкция позволяет повторно использовать пул приложений, поддерживая автоматическое сохранение и возобновление длительной работы.

Вы можете запускать задачи в фоновом режиме, и они будут выполнены даже после завершения запроса. Не допускайте возникновения неперехваченного исключения.Обычно вы хотите всегда создавать свои исключения.Если исключение будет сгенерировано в новом потоке, то это приведет к сбою Рабочий процесс IIS - w3wp.exe, потому что вы больше не находитесь в контексте запроса.Это также приведет к остановке любых других выполняемых вами фоновых задач в дополнение к сеансам с сохранением памяти в процессе, если вы их используете.Это было бы трудно диагностировать, вот почему такая практика не рекомендуется.

Просто создайте суррогатный процесс для запуска асинхронных задач;это не обязательно должна быть служба Windows (хотя в большинстве случаев это более оптимальный подход.MSMQ - это намного больше, чем kill.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top