Длительные операции (потоки) в веб-среде (asp.net) - PullRequest
10 голосов
/ 11 мая 2010

У меня есть сайт asp.net (mvc). В качестве части функций мне придется поддерживать некоторые длительные операции, например:

Инициировано пользователем: Пользователь может загрузить (XML) файл на сервер. На сервере мне нужно извлечь файл, сделать некоторые манипуляции (вставить в БД) и т.д. ... Это может занять от одной минуты до десяти минут (или даже больше - зависит от размера файла). Конечно, я не хочу блокировать запрос во время выполнения импорта, но я хочу перенаправить пользователя на страницу прогресса, где у него будет возможность наблюдать за состоянием, ошибками или даже отменять импорт.

Эта операция будет использоваться не часто, но может случиться, что два пользователя одновременно попытаются импортировать данные. Было бы неплохо запустить импорт параллельно. Сначала я думал создать новый поток в iis (действие контроллера) и запустить импорт в новом потоке. Но я не уверен, если это хорошая идея (для создания рабочих потоков на веб-сервере). Должен ли я использовать службы Windows или любой другой подход?

Инициировано из системы: - Мне придется периодически обновлять индекс Lucene с новыми данными. - Мне придется отправлять массовые электронные письма (в будущем).

Должен ли я реализовать это как задание на сайте и запустить задание через Quartz.net, или я также должен создать службу Windows или что-то еще?

Каковы наилучшие практики для запуска "заданий" сайта?

Спасибо!

Ответы [ 3 ]

6 голосов
/ 11 мая 2010

Я бы внедрил автономную службу Windows для долгосрочных задач. Веб-приложение делегирует долго выполняющиеся задачи этому сервису посредством подхода очереди. Вам решать, как вы будете организовывать очередь задач. Будет ли задача в очереди иметь приоритет, максимальное время выполнения или нет. Очередь может быть реализована как обычная таблица (таблицы) в СУБД, которая содержит свойства информации о состоянии выполнения задания (довольно простой подход).

Так что общий сценарий может выглядеть следующим образом:

  • Клиент отправляет всю необходимую информацию на веб-сервер

  • Веб-сервер делегирует задание на обслуживание и уведомляет клиента - задача была успешно поставлен в очередь (идентификатор задачи также отправляется клиенту)

  • Внешняя служба начинает обработку задачи, обновление информации о ходе выполнения.

  • Клиент начинает опрос веб-сервера с короткие запросы на выполнение задания (с идентификатором, полученным ранее) статус и прогресс.

Вы можете выбрать разные технологии (Windows Service + DB / WCF Service) и разные подходы к коммуникации (опрос, отправка, обратные вызовы), но я советую делегировать долгосрочные задачи внешней службе (не выполнять их в веб-приложении).

Выполнение длительных задач приводит к модели потока на запрос (в терминах многопоточного программирования). Эта модель имеет плохую масштабируемость и ограничивает максимальное количество потоков в пуле потоков. Это не ваш случай, хотя :)

4 голосов
/ 11 мая 2010

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

1 голос
/ 11 мая 2010

Я успешно реализовал сценарий, подобный этому, используя jQuery. в основном я использую функцию beforeSend: для отображения страницы типа «пожалуйста, подождите». Вот основной код в игре (также я не могу, вы также можете использовать базовый класс AsyncController для выполнения асинхронного действия):

<script type="text/javascript">
    $(document).ready(function() {
        $('#create').bind('click', function() {
            saveFundProperty();
        });
    });

    // main functions
    function saveFundProperty() {
        var url = '<%= Url.Action("Create", "FundProperty") %>';
        var params = { fundId: $("#FundID").val(), propertyId: $("#PropertyID").val() };
        SendAjax(url, params, beforeQuery, saveFundPropertyResponse);
    }

    function beforeQuery() {
        var url = '<%= Url.Action("Wait", "FundProperty") %>';
        $("#statusMsg").load(url);
    }

    function saveFundPropertyResponse(data) {
        if (data.length != 0) {
            if (data.indexOf("ERROR:") >= 0) {
                $("#statusMsg").html(data).css('backgroundColor','#eeaa00');
            }
            else {
                $("#statusMsg").html(data);
            }
        }
    }
</script>

надеюсь, это поможет.

метод SendAjax - это просто функция-обертка, которая делает вещи немного более непротиворечивыми. здесь это в полном объеме:

<script type="text/javascript">
function SendAjax(urlMethod, jsonData, beforeSendFunction, returnFunction, dataType, contentType) {
    $.ajaxSetup({ cache: false });
    dataType = dataType || "text"; // default return type
    contentType = contentType || "application/x-www-form-urlencoded"; // default input type
    $.ajax({
        type: "POST",
        url: urlMethod,
        data: jsonData,
        dataType: dataType,
        contentType: contentType,
        beforeSend: function() {
            if(beforeSendFunction!==null)
                beforeSendFunction();
        },
        success: function(data) {
            // Do something interesting here.
            if (data != null && returnFunction!==null) {
                returnFunction(data);
            }
        },
        error: function(xhr, status, error) {
            // Boil the ASP.NET AJAX error down to JSON.
            var err = eval("(" + xhr.responseText + ")");

            // Display the specific error raised by the server
            alert(err.Message);
        }
    });
}
</script>

[править] - не уверен, что происходит с форматированием SendAjax. надеюсь, что это легко скопировать / вставить ...

...