小白最近做的專案當中涉及到了WebApi,具體是三方程式透過呼叫小白寫的 WebApi 進行推送資料,呼叫的次數、頻率是沒有限制的,因此就涉及到了併發。
小白使用.NET 8 建立了WebApi,在讀取Request請求時,使用如下的方式:
1 var requestContent = string. Empty; 2 using (var stream = new MemoryStream()) 3 using (var reader = new StreamReader(stream)) 4 { 5 await Request.Body.CopyToAsync(stream); 6 stream.Seek(0, SeekOrigin.Begin); 7 requestContent = await reader.ReadToEndAsync(); 8 }
使用這種方式,在Request的Body資料量很大並且兩個請求同時發起時,會導致在Request的Body未讀取完畢時,便已經釋放從而導致程式報錯。
小白一開始的解決方法是設定Request Body的最大數量,然而並沒有什麼效果。後來小白考慮是不是將介面改為同步的方式,以達到處理完一個請求再處理另一個請求的目的,這樣是不是就不會有提前釋放的錯誤了。
然而雖然介面是同步的方式,但是外部請求來的時候伺服器還是會同時處理多個請求。這就引發了小白對IIS處理併發請求的思考,事實上IIS 的執行緒池會分配適當數量的執行緒來並行處理這些請求,因此不管介面是否同步,都會並行處理。
並且同步的方式效能並不是很好,微軟也不建議這麼操作,當程式流量比較大的時候,很容易導致程式不穩定甚至崩潰。
經過小白一番努力最後發現,多個介面裡面都用到了上述的程式碼去讀取Request.Body。所以首先將這段程式碼封裝未一個方法,以達到最佳化程式碼的目的。
關鍵的解決方法如下:
StreamReader stream = new StreamReader(Request.Body); string body = stream.ReadToEndAsync().GetAwaiter().GetResult();
不使用MemoryStream,直接使用StreamReader讀取,同時將ReadToEndAsync變為同步方法。雖然小白沒有真正弄清楚原因,但是上面的方法成功解決了小白程式的bug。
具體的原理,小白希望有熱愛編碼之士仗義出手指教一下。或者小白在程式設計領域越來越強之後,再回過頭來,對其中原理解釋一番。