我正在使用webApi休息服务的“发布”异步方法:
public async Task<object> Post([FromBody]string data)
{
object response = ExecuteServerLogics(data);
return response;
}
以上代码效果很好,但在某些客户的电话中,我们遇到了性能问题。
在这里阅读了一些文章后,我注意到我们的webApi休息服务, 并没有真正与其传入的Web请求异步工作, 因为我们忘了使用 async / await pattern :
public async Task<object> Post([FromBody]string data)
{
object response = await Task<object>.Run( () =>
{
return ExecuteServerLogics(data);
});
return response;
}
在此修复之后,我们注意到性能变得更好, 但我们发现另一个批评问题: 访问 HttpContext.Current 时,它返回Null引用:
public async Task<object> Post([FromBody]string data)
{
object response = await Task<object>.Run( () =>
{
var currentContext = HttpContext.Current; // Returns Null!
return ExecuteServerLogics(data);
});
return response;
}
我们试图找到一个解决方案,在大多数帖子中我们发现我们应该通过 工作线程的 HttpContext 引用执行服务器逻辑的内部任务。 这个解决方案的问题是服务器的逻辑方法,使用许多使用的静态类 “HttpContext.Current”,例如 -
因此,传递工作线程的“HttpContext.Current”引用将无法解决它。
当我们尝试下一个解决方案时:
public async Task<object> Post([FromBody]string data)
{
// Save worker context:
var currentContext = HttpContext.Current;
object response = await Task<object>.Run( () =>
{
// Set the context of the current task :
HttpContext.Current = currentContext ; // Causes the calls not to work asynchronously for some reason!
// Executes logics for current request:
return ExecuteServerLogics(data);
});
return response;
}
出于某种原因,我们注意到性能再次恶化,就像它再次同步恢复工作一样。
我们的问题是:
1。为什么在上一个示例中,在await任务中设置“HttpContext.Current”, 导致请求返回与同步结果类似的相同的糟糕性能结果?
2. 还有另一种方法可以在调用的内部任务 - “ExecuteServerLogics”中使用“HttpContext.Current”, 并且在所有静态类中也调用“HttpContext.Current”? 我在某种程度上做错了整个设计吗?
谢谢!
答案 0 :(得分:12)
从一开始:
public async Task<object> Post([FromBody]string data)
{
object response = ExecuteServerLogics(data);
return response;
}
不要忽略编译器警告;编译器将为此方法生成一个警告,明确指出它将同步运行。
继续前进:
在一些客户的电话中,我们遇到了性能问题。
服务器上的异步代码不对单个调用的隔离速度更快。它只能帮助您扩展您的服务器。
特别是,Task.Run
会否定async
的所有性能优势,然后将性能降低一点。我相信你所测量的性能改善是巧合。
在大多数帖子中,我们发现我们应该将工作线程的HttpContext引用传递给执行服务器逻辑的内部任务。
这些帖子错了。恕我直言。当该对象专门设计为仅从请求线程访问时,您最终使用后台线程中的HttpContext
对象。
我在某种程度上做错了整个设计吗?
我建议你退一步思考大局。当请求进入时,它有一定的工作量。这项工作是同步还是异步完成对客户来说无关紧要;两种方法都需要大约相同的时间。
如果您需要将早期返回给客户端,那么您需要一个完全不同的架构。通常的方法是将工作排队到可靠的队列(例如,Azure队列),具有单独的后端(例如,Azure WebRole),并在工作完成时主动通知客户端(例如,SignalR)。
但是,并不是说async
无用。如果ExecuteServerLogics
是一个I / O绑定方法,那么它应该是异步而不是阻塞,然后你可以使用异步方法:
public async Task<object> Post([FromBody]string data)
{
object response = await ExecuteServerLogicsAsync(data);
return response;
}
这将使您的服务器整体上更具响应性和可扩展性(即,不会被许多请求所淹没)。
答案 1 :(得分:2)
如果你的任务在你的ApiController派生类中,你可以使用:
var ctx = this.Request.Properties["MS_HttpContext"] as System.Web.HttpContextWrapper;
这将为您提供一个包含所有常用属性的HttpContext包装器。
答案 2 :(得分:0)
阿米尔(Amir),我认为您正在寻找以下类似内容。我一直在处理同一问题,试图优化一系列通话。它需要一直是异步的,这意味着ExecuteServerLogics()必须是异步的,并且还必须将包含的lamda标记为异步。
我相信,遵循该模式,您可以消除大多数性能问题。很高兴这样传递上下文。
public async Task<object> Post([FromBody]string data)
{
// Save worker context:
var currentContext = HttpContext.Current;
object response = await Task<object>.Run(async () =>
{
// Set the context of the current task :
HttpContext.Current = currentContext ;
// Executes logics for current request:
return await ExecuteServerLogics(data);
});
return response;
}