Длительные операции (потоки) в среде web (asp.net)

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

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

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

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

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

Каковы наилучшие методы работы с рабочими местами?

Спасибо!

Ответ 1

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

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

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

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

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

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

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

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

Ответ 2

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

Ответ 3

Я успешно реализовал сценарий, подобный этому, используя 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>

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