2011-05-31 95 views
1

我已经遇到this thread了,但我可能需要其他的东西来处理我的情况。设置控制器动作超时

我有一个返回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(); 
} 

正如评论暗示,内部运行有很长的任务e控制器。 (这是一个文档生成模块,它将PDF上传到Azure blob存储并将其链接返回到视图。)

这在我的开发机器中工作正常,但是当它在Azure生产中生效时环境,它超时。我在各处都输入了大量的日志条目,事实证明,它能够上载文档并返回到控制器(即它到达上面的控制器返回语句)。但是,当模型数据返回到视图时,客户端脚本不会回叫(即div内容不会被结果替换)。

有没有办法以某种方式延长呼叫的超时?在我的(不安全的)本地环境中复制是很困难的,所以最终的修复将会有所帮助。

如果我在GetFoo()方法上使用属性[AsyncTimeout(3600)],则此操作永远不会从UI调用。

任何建议将不胜感激。

回答

3

的问题是,在Azure负载平衡器有它自己的超时设置为一分钟。任何需要更长时间的请求than a minute gets terminated。没有办法改变这一点。

在Azure环境中解决这个问题的方法是让一个ajax调用启动进程并返回某种进程ID,然后让客户端轮询另一个ajax调用来传递此进程ID以查看它是否完成。它可能看起来像这样未编译和未经测试的代码。在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; 
} 

希望这会给你一个起点。

+0

这很有趣。我会尝试,如果'Server.ScriptTimeout'将无法正常工作。谢谢。 – 2011-06-01 01:34:33

+0

你有这个ajax-polling-passing-id技术的例子吗?我试图弄清楚它在我的情况下会如何工作。 – 2011-06-02 08:48:37

+0

好的,我添加了一个相当广泛的例子。它不会按原样运行,但它应该足以为您提供一个起点。 – knightpfhor 2011-06-02 20:54:24

2

要看看这个

回答你的问题是:AsyncTimeout(3600000)

多看这里 Controller Timeout MVC 3.0

+0

如果我在控制器方法上使用'[AsyncTimeout(3600000)]'属性,那么这个动作永远不会从jQuery'$ .post()'中调用。 – 2011-05-31 09:06:37

+0

这不应该是问题,如果是的话,你可以使用这个你设置超时的地方:> article:http://codebetter.com/petervanooijen/2006/06/15/timeout-of-an-asp-net-页面/还要确保数据库也没有超时。 – cpoDesign 2011-05-31 09:32:22

+0

@cpoDesign,我会给'Server.ScriptTimeout = 3600;'尝试像你的链接,并让你知道。它与我上面提到的线程类似。 – 2011-05-31 10:00:29

1

要使用异步控制器,控制器必须从AsyncController继承:

public class WebsiteController : AsyncController 

,然后使用异步方法的任何行动必须使用的

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

的格式,其中ActionName的名称是您动作,而不是异步功能使用

AsyncManager.OutstandingOperations.Increment(); 

each tim Ë启动新aysnchronous方法和

AsyncManager.OutstandingOperations.Decrement(); 

当方法完成,毕竟优秀的操作已经完成,它会沿着已完成的功能移动(请务必注明您需要在完成函数的参数异步功能,所以它知道要传递什么)

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

然后使用AsyncTimeout属性实际上会影响该函数。我不确定如果您尝试将该属性应用于不在异步控制器中的操作会发生什么情况。

这些更改不需要更改任何对JavaScript中操作的引用,也不需要您仍然只是请求“ActionName”,它会查看是否有Async/Completed安装版本和如果不是,它会寻找正常的行动,并使用它发现的任何一个。