2011-05-31 97 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安裝版本和如果不是,它會尋找正常的行動,並使用它發現的任何一個。