Web API 服務 - 如何在非同步任務中使用“HttpContext.Current”
我正在使用 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”**的靜態類,例如 -
- 記錄器呼叫。
- 檢索 user.identity 的靜態安全類
- 檢索傳入請求的會話數據等的靜態安全類。
因此,傳遞工作執行緒的**“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; }這將使您的伺服器在整體上更具響應性和可擴展性(即,不會被許多請求淹沒)。