Asp.net

Web API 服務 - 如何在非同步任務中使用“HttpContext.Current”

  • August 12, 2018

我正在使用 webApi 休息服務的“發布”非同步方法:

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

     return response;
}

上面的程式碼執行良好,但在某些客戶端呼叫中,我們遇到了性能問題。

在這裡閱讀了一些文章後,我注意到我們的 webApi 休息服務並沒有真正與其傳入的網路請求非同步工作,因為我們忘記了使用async/await 模式

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”**的靜態類,例如 -

  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;
   }

出於某種原因,我們注意到性能再次變差,就像它又恢復了同步工作一樣。

我們的問題是:

1.為什麼在上一個例子中,在 await 任務中設置“HttpContext.Current”,會導致請求返回同樣糟糕的性能結果,類似於同步結果?

2.是否有另一種方法可以在呼叫“ExecuteServerLogics”的內部任務中以及在所有也呼叫“HttpContext.Current”的靜態類中使用 HttpContext.Current”?我在某種程度上做錯了整個設計嗎?

謝謝!

從一開始就:

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;
}

這將使您的伺服器在整體上更具響應性和可擴展性(即,不會被許多請求淹沒)。

引用自:https://stackoverflow.com/questions/23790292