Установить время ожидания для действия контроллера - PullRequest
1 голос
/ 31 мая 2011

Я уже сталкивался с этой темой , но мне может понадобиться что-то еще для моей ситуации.

У меня есть действие, которое возвращает ViewResult, которое вызывается клиентом $.post()

JavaScript:

var link = 'GetFoo?fooBar=' + fooBar;

var jqxhr = $.post(link, function (response) {
    $('#myDiv').replaceWith(response);
});

Контроллер:

public ViewResult GetFoo(String fooBar)
{
    if (Request.IsAjaxRequest())
    {
        // perform a ridiculously long task (~12 minutes)           
        // algorithm: 1) download files from the Azure blob storage
        // 2) update each file
        // 3) reupload to blob storage
        // 4) return a list of URIs to be displayed to the UI
        return View("MyFooView", data);
    }
    throw new InvalidOperationException();
}

Как следует из комментария, внутри контроллера выполняется длинная задача. (Это модуль создания документов, который загружает PDF-файлы в хранилище BLOB-объектов Azure и возвращает ссылку на него в View.)

Это нормально работает на моей машине разработчика, но когда она запускается в (безопасной) производственной среде Azure, время ожидания . Я поместил много записей в журналы везде, и, как оказалось, он может загружать документы и возвращаться в контроллер (то есть он достигает оператора возврата контроллера выше). Однако когда приходит время вернуть данные модели в представление, клиентский сценарий не перезванивается (т. Е. Содержимое div не заменяется результатами).

Есть ли способ как-то продлить тайм-аут вызова? Трудно воспроизвести в моей (небезопасной) локальной среде, поэтому поможет окончательное исправление.

Если я использую атрибут [AsyncTimeout(3600)] в своем методе GetFoo(), то это действие никогда не будет вызвано из пользовательского интерфейса.

Будем благодарны за любые предложения.

Ответы [ 3 ]

3 голосов
/ 31 мая 2011

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

Обходной путь в среде Azure состоит в том, чтобы один вызов ajax запустил процесс и возвратил какой-то идентификатор процесса, а затем запросил у клиента другой вызов ajax для передачи этого идентификатора процесса, чтобы проверить, завершен ли он. Это может выглядеть примерно так, как этот не скомпилированный и непроверенный код. В JavaScript:

var link = 'BeginFooProcessing?fooBar=' + fooBar;

var jqxhr = $.post(link, function (response) {
    var finishedlink = 'CheckFooFinished?fooId=' + response;

    // Check to see if we're finished in 1 second
    setTimeout("CheckIfFinishedYet('" + finishedlink + "')", 1000);
});

function CheckIfFinishedYet(finishedlink) {
    var response = $.post(finishedlink, function (response) {
        if (response == null) {
            // if we didn't get a result, then check in another second
            setTimeout("CheckIfFinishedYet('" + finishedlink + "')", 1000);
        }
        else {
            // Yay, we've got a result so we'll just write it out
            $('#myDiv').replaceWith(response);
        }
    });
}

А в вашем контроллере:

public ViewResult BeginFooProcessing(String fooBar)
{
    if (Request.IsAjaxRequest())
    {
        Guid fooId = Guid.NewGuid();

        var result = new FooResult
                        {
                            FooId = fooId,
                            HasFinishedProcessing = false,
                            Uris = new List<string>()
                        };

        // This needs to go to persistent storage somewhere
        // as subsequent requests may not come back to this
        // webserver
        result.SaveToADatabaseSomewhere();

        System.Threading.Tasks.Task.Factory.StartNew(() => ProcessFoo(fooId));


        return View("MyFooStartView", fooId);
    }
    throw new InvalidOperationException();
}

private void ProcessFoo(Guid fooId)
{
    // Perform your long running task here

    FooResult result = GetFooResultFromDataBase(fooId);

    result.HasFinishedProcessing = true;
    result.Uris = uriListThatWasCalculatedAbove;

    result.SaveToADatabaseSomewhere();
}

public ViewResult CheckFooFinished(Guid fooId)
{
    if (Request.IsAjaxRequest())
    {
        FooResult result = GetFooResultFromDataBase(fooId);

        if (result.HasFinishedProcessing)
        {
        // Clean up after ourselves
        result.DeleteFromDatabase();

            return View("MyFooFinishedView", result.Uris);
        }

        return View("MyFooFinishedView", null);

    }
    throw new InvalidOperationException();
}

private class FooResult
{
    public Guid FooId { get; set; }

    public bool HasFinishedProcessing { get; set; }

    public List<string> Uris;
}

Надеюсь, это даст вам отправную точку.

2 голосов
/ 31 мая 2011

вы хотите посмотреть на это

ответ на ваш вопрос: [AsyncTimeout (3600000)]

подробнее здесь Тайм-аут контроллера MVC 3.0

1 голос
/ 01 февраля 2012

Чтобы использовать асинхронные контроллеры, ваш контроллер должен наследовать от AsyncController:

public class WebsiteController : AsyncController

И тогда любое действие, использующее асинхронные методы, должно использовать формат

public void ActionNameAsync(int param)
public ActionResult ActionNameCompleted(int param)

Где ActionName - этоимя вашего действия и вместо функции Async используйте

AsyncManager.OutstandingOperations.Increment();

каждый раз, когда вы запускаете новый асинхронный метод, и

AsyncManager.OutstandingOperations.Decrement();

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

AsyncManager.Parameters["param"] = "my name";

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

Эти изменения не потребуют изменения каких-либо ссылок на действие в javascript ичто не так, как вы все равно просто запросили бы «ActionName», и он посмотрел бы, существует ли установочная версия Async / Completed, и если нет, он будет искать только это обычное действие и использовать то, что найдет.

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