Web API服务 - 如何在异步任务中使用"HttpContext.Current"

Ami*_*nja 8 asp.net asynchronous httpcontext async-await asp.net-web-api

我正在使用webApi休息服务的"Post"异步方法:

public async Task<object> Post([FromBody]string data)
{
      object response = ExecuteServerLogics(data);

      return response;
}
Run Code Online (Sandbox Code Playgroud)

上面的代码工作得很好,但在一些客户的电话中,我们遇到了性能问题.

在这里阅读了一些文章后,我注意到我们的webApi休息服务并没有真正与其传入的Web请求异步工作,因为我们忘了使用async/await模式:

public async Task<object> Post([FromBody]string data)
{
      object response = await Task<object>.Run( () =>
      {
           return ExecuteServerLogics(data);
      });

      return response;
}
Run Code Online (Sandbox Code Playgroud)

在这个修复之后,我们注意到性能变得更好,但是我们发现了另一个批评问题:当访问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;
}
Run Code Online (Sandbox Code Playgroud)

我们试图为它找到一个解决方案,在大多数帖子中我们发现我们应该将工作线程的HttpContext引用传递给执行服务器逻辑的内部任务.这个解决方案的问题是服务器的逻辑方法,使用许多使用"HttpContext.Current"的静态类, 如 -

  1. 记录器调用.
  2. 检索user.identity的静态安全性类
  3. 静态安全类,用于检索传入请求的会话数据等.

因此,传递工作线程的"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;
    }
Run Code Online (Sandbox Code Playgroud)

出于某种原因,我们注意到性能再次恶化,就像它再次同步返回工作一样.

我们的问题是:

1.为什么在上一个例子中,在await任务中设置"HttpContext.Current"会导致请求返回与同步结果类似的相同不良性能结果?

2.还有另一种方法可以在内部任务中使用"HttpContext.Current"调用 - "ExecuteServerLogics",并在所有静态类中调用"HttpContext.Current"吗?我以某种方式做错了整个设计吗?

谢谢!

Ste*_*ary 12

从最开始:

public async Task<object> Post([FromBody]string data)
{
  object response = ExecuteServerLogics(data);
  return response;
}
Run Code Online (Sandbox Code Playgroud)

不要忽略编译器警告; 编译器将为此方法生成警告,该警告明确指出它将同步运行.

继续:

在一些客户的电话中,我们遇到了性能问题.

对于单独的呼叫,服务器上的异步代码不会更快.它只能帮助您扩展服务器.

特别是,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;
}
Run Code Online (Sandbox Code Playgroud)

这将使您的服务器整体上更具响应性和可扩展性(即,不会被许多请求所淹没).

  • 当你使用`async`和`await`时,`HttpContext.Current`会适当地流动.如果您明确使用后台线程,例如`Task.Run`,它就不会流动. (2认同)
  • 这不再是原始问题的真正原因. (2认同)