设置控制器操作超时
本文关键字:超时 操作 控制器 设置 | 更新日期: 2023-09-27 17:57:57
我已经遇到了这个线程,但我可能需要其他东西来解决我的问题。
我有一个返回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上传到Azure blob存储,并将其链接返回到视图。)
这在我的开发机器中运行良好,但当它在(安全的)Azure生产环境中运行时,它会超时。我到处都放了很多日志条目,事实证明,它能够上传文档并返回到控制器(即,它达到上面的控制器返回语句)。但是,当需要将模型数据返回到View时,客户端脚本不会被回调(即div
内容不会被结果替换)。
有没有办法以某种方式延长呼叫的超时时间?在我(不安全的)当地环境中很难繁殖,所以最终的解决方案会有所帮助。
如果我在GetFoo()
方法上使用属性[AsyncTimeout(3600)]
,那么这个操作永远不会从UI调用。
如有任何建议,我们将不胜感激。
问题是Azure负载均衡器有自己的超时,设置为一分钟。任何超过一分钟的请求都会被终止。没有办法改变这一点。
在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;
}
希望这能给你一个起点。
你想看看这个
您的问题的答案是:[AncTimeout(3600000)]
在此处查看更多信息控制器超时MVC 3.0
要使用异步控制器,您的控制器必须从异步控制器继承:
public class WebsiteController : AsyncController
然后任何使用异步方法的操作都必须使用格式
public void ActionNameAsync(int param)
public ActionResult ActionNameCompleted(int param)
其中ActionName是操作的名称,使用而不是Async函数
AsyncManager.OutstandingOperations.Increment();
每次你开始一个新的aysnchronous方法和
AsyncManager.OutstandingOperations.Decrement();
当方法完成时,在所有未完成的操作完成后,它将移动到completed函数(确保在异步函数中指定已完成函数所需的参数,这样它就知道要传递什么)
AsyncManager.Parameters["param"] = "my name";
那么使用AsyncTimeout属性实际上会影响函数。我不确定如果您尝试将该属性应用于不在异步控制器中的操作会发生什么。
这些更改不需要更改javascript中对操作的任何引用,因为你仍然只需要请求"ActionName",它会查看是否有Async/Completed安装版本,如果没有,它只会查找正常的操作并使用它找到的任何操作。