Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
160 views
in Technique[技术] by (71.8m points)

c# - Set Timeout For Controller Action

I have come across this thread already, but I might need something else for my situation.

I have an action that returns a ViewResult, which is called by the client's $.post()

JavaScript:

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

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

Controller:

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

As the comment implies, there is long task running inside the Controller. (This is a document generation module that uploads PDFs to the Azure blob storage and returns a link to it to the View.)

This is working fine in my dev machine but when it goes live in a (secure) Azure production environment, it times out. I have put in lots of logging entries everywhere and as it turns out, it is able to upload the documents and return to the controller (i.e. it reaches the controller return statement above). However, when it is time to return the model data to the View, the client script doesn't called back (i.e. the div content doesn't get replaced with the results).

Is there a way to somehow prolong the timeout of the call? It is difficult to reproduce in my (unsecure) local environment so a definitive fix will help.

If I use the attribute [AsyncTimeout(3600)] on my GetFoo() method, then this action never gets called from the UI.

Any suggestions will be appreciated.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem is that the Azure load balancer has it's own timeout which is set to one minute. Any request that takes longer than a minute gets terminated. There is no way to change this.

The way around this in the Azure environment is to have one ajax call start the process and return some sort of process ID then have the client poll another ajax call to passing in this process ID to see if it's complete. It might looks something like this uncompiled and untested code. In 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);
        }
    });
}

And in your controller:

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

Hopefully that will give you a starting point.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...