生活随笔
收集整理的這篇文章主要介紹了
又一次Task.Wait引起的教训
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
????????最近一用戶在使用BeetleX.HttpClient組件并發訪問延時比較高的https服務時引起了卡死現像。由于組件更多使用場景是內部服務和非https,一直沒有這情況出現;但用戶提供測試場景下這情況必現,所以翻查了一些相關代碼。
protected virtual void OnSslAuthenticate(SslStream sslStream){Task task;if (SslProtocols == null)SslProtocols = System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Tls11 |System.Security.Authentication.SslProtocols.Tls12;task = sslStream.AuthenticateAsClientAsync(SslServiceName, CertificateCollection.Count > 0 ? CertificateCollection : null, SslProtocols.Value, false);task.Wait();}
BeetleX的tcpclient中使了無限超時來等待ssl驗證環節,正常局域網環境這個等待是不會卡死的,畢竟網內環境良好要么成功要因異常觸發取消等。但在網絡差的環境下就存在問題了,socket的異步receive無法觸發異常引起這個Wait把線程永久掛起了,當一段時間太多這種情況出現那非常的事情就是大量線程被這個Wait抽干導致服務不能正常工作。
????????為了解決這些比較特殊的情況加上超時時間就好了
protected virtual void OnSslAuthenticate(SslStream sslStream){Task task;if (SslProtocols == null)SslProtocols = System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Tls11 |System.Security.Authentication.SslProtocols.Tls12;task = sslStream.AuthenticateAsClientAsync(SslServiceName, CertificateCollection.Count > 0 ? CertificateCollection : null, SslProtocols.Value, false);if (!task.Wait(5000)){throw new BeetleX.BXException($"connect {mIPAddress}:{mPort} SSL Authenticate timeout!");}}
把超時設置成5秒,在創建連接5秒后ssl還沒有握手成功就直接超時關閉連接重新創建。
????????基礎的問題解決了,但httpclientpool層面還有一個問題需要處理。當連接池一開始處于滿負載請求,同時創建大量的Client會引起大量線程等待導致服務不太穩定(畢竟在不很多穩定的網絡環境下ssl握手時間有些長)。為了解決問題使用一個指定的線程數的隊列來創建ssl握手處理機制,這樣就可以避免同時創建大量連接線程引起短暫卡殼現象。
public Task<HttpClientHandler> Pop(){HttpClientHandler result;TaskCompletionSource<HttpClientHandler> completionSource;lock (this){if (mPools.Count > 0){result = mPools.Pop();result.Using = true;result.TimeOut = BeetleX.TimeWatch.GetElapsedMilliseconds() + TimeOut;return Task.FromResult(result);}if (Clients.Count > MaxConnections){if (mWaitQueue.Count < MaxWaitLength){completionSource = new TaskCompletionSource<HttpClientHandler>();mWaitQueue.Enqueue(completionSource);return completionSource.Task;}else{throw new HttpClientException($"Request {Host} connections limit");}}}completionSource = new TaskCompletionSource<HttpClientHandler>();mCreateDispatchCenter.Next().Enqueue(new CreateClientTask { ClientHandlerPool = this, CompletionSource = completionSource });return completionSource.Task;}struct CreateClientTask{public TaskCompletionSource<HttpClientHandler> CompletionSource;public HttpClientHandlerPool ClientHandlerPool;}private static BeetleX.Dispatchs.DispatchCenter<CreateClientTask> mCreateDispatchCenter= new Dispatchs.DispatchCenter<CreateClientTask>(OnProcessCreateClient, 20);private static void OnProcessCreateClient(CreateClientTask e){try{var result = e.ClientHandlerPool.Create();result.Using = true;result.TimeOut = BeetleX.TimeWatch.GetElapsedMilliseconds() + e.ClientHandlerPool.TimeOut;e.CompletionSource.TrySetResult(result);}catch (Exception e_){e.CompletionSource.TrySetException(e_);}}
簡單修改一下連接池Pop方法的代碼就可以了。
BeetleX
開源跨平臺通訊框架(支持TLS)
提供高性能服務和大數據處理解決方案
https://beetlex.io
總結
以上是生活随笔為你收集整理的又一次Task.Wait引起的教训的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。