最近我們的介面老是會出現BadHttpRequestException例外,但是手動查看報錯的頁面卻一點問題沒有,很奇怪,后來仔細研究這個例外,例外原因其實根據例外物件本身就已經能分析出來了(由于第一次遇到不清楚怎么造成的,尷尬),就是由于無法正常讀取Http-Request請求物件里的內容導致的例外!但是什么情況下會導致這個情況,不得而知, 后來百度其他朋友的文章,終于了解到發生當前錯誤的原因:
是由于當請求出現并發,而請求的執行緒池又不夠使用,前面的請求阻塞后面的請求時,后面的請求強制取消后就會導致后面的請求在被接收程式處理Request內容時發生例外,即:BadHttpRequestException
先看下介面是怎么接收的:
[HttpPost] public async Task<IActionResult> PPIFrameInit([FromBody] PaypalClientRequest parames) { //... }
很顯然,介面采用的是自動系結Model模型Request-Body的方式,那么如果按照上述請求就很容易發生下面的例外了,
例外內容如下:
ClientIP:157.185.158.160 URL:http://www.coowigsby.com/ajax/paydd/PPIFrameInit Type:Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException Msg:Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Unexpected end of request content. at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelBadHttpRequestException.Throw(RequestRejectionReason reason) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1ContentLengthMessageBody.ReadAsyncInternal(CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.ReadAsyncInternal(Memory`1 destination, CancellationToken cancellationToken) at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken) at Microsoft.AspNetCore.WebUtilities.StreamHelperExtensions.DrainAsync(Stream stream, ArrayPool`1 bytePool, Nullable`1 limit, CancellationToken cancellationToken) at Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext bindingContext) at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value, Object container) at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at MeShop.CacheResponse.CacheResponseMiddleware.Invoke(HttpContext httpContext) in /builds/sitegroup/meshop/meshop/MeShop.CacheResponse/CacheResponseMiddleware.cs:line 56 at MeShop.View.Shop.Models.CheckExpirationMiddleware.Invoke(HttpContext httpContext) in /builds/sitegroup/meshop/meshop/MeShop.View.Shop/Models/CheckExpirationMiddleware.cs:line 38 at MeShop.WebCommon.Cache.ResponseDistributedCacheMiddleware.Invoke(HttpContext httpContext) in /builds/sitegroup/meshop/meshop/MeShop.WebCommon/Cache/ResponseDistributedCacheMiddleware.cs:line 105 at Microsoft.AspNetCore.ResponseCaching.ResponseCachingMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at MeShop.WebCommon.CircuitBreakerWebMiddleware.<>c__DisplayClass13_0.<<InvokeAsync>b__1>d.MoveNext() in /builds/sitegroup/meshop/meshop/MeShop.WebCommon/Middleware/CircuitBreakerWebMiddleware.cs:line 246 --- End of stack trace from previous location --- at Polly.AsyncPolicy.<>c__DisplayClass40_0.<<ImplementationAsync>b__0>d.MoveNext() --- End of stack trace from previous location --- at Polly.CircuitBreaker.AsyncCircuitBreakerPolicy.<>c__DisplayClass8_0`1.<<ImplementationAsync>b__0>d.MoveNext() --- End of stack trace from previous location --- at Polly.CircuitBreaker.AsyncCircuitBreakerEngine.ImplementationAsync[TResult](Func`3 action, Context context, CancellationToken cancellationToken, Boolean continueOnCapturedContext, ExceptionPredicates shouldHandleExceptionPredicates, ResultPredicates`1 shouldHandleResultPredicates, ICircuitController`1 breakerController) at Polly.CircuitBreaker.AsyncCircuitBreakerPolicy.ImplementationAsync[TResult](Func`3 action, Context context, CancellationToken cancellationToken, Boolean continueOnCapturedContext) at Polly.AsyncPolicy.ExecuteAsync(Func`3 action, Context context, CancellationToken cancellationToken, Boolean continueOnCapturedContext) at MeShop.WebCommon.CircuitBreakerWebMiddleware.InvokeAsync(HttpContext context) in /builds/sitegroup/meshop/meshop/MeShop.WebCommon/Middleware/CircuitBreakerWebMiddleware.cs:line 275
這里列舉一下本地除錯的復現步驟(本地除錯是單執行緒):
- 在請求處理程序的某處設定斷點
- 在PostMan或瀏覽器上先發送A請求,命中斷點,不繼續往下處理,讓請求阻塞
- 繼續發放B請求,這時請求阻塞中,然后點擊取消請求
- 釋放斷點繼續處理,結果是A請求正常處理結束,B請求處理時拋出例外
解決方法有三種(參考的博客里只介紹了一種):
- 1_可選擇在全域例外過濾器中不處理該例外,不列印錯誤日志(有掩耳盜鈴的嫌疑,不推薦);
- 2_可以將介面[FromBody]由自動系結改成代碼讀取接收,使用try-catch捕獲例外單獨處理(跟1類似,也不推薦);
- 3_可以增加最小執行緒池數量,使其能夠擁有足夠的執行緒處理并發請求(確實訪問量太大,可以使用);
- 4_優化現有的代碼:分析當時的請求為何造成了執行緒不夠用的原因,把耗時較長造成阻塞的有問題的方法處理掉,更新后再繼續觀察(適用于訪問量沒有那么大,部分介面代碼質量不好的情況時使用);
參考博客:關于.Net Core3.0下因客戶端主動取消請求導致的Request.Body例外
*感謝您的閱讀,喜歡的、有用的就請大哥大嫂們高抬貴手“推薦一下”吧!你的精神 支持是博主強大的寫作動力,歡迎轉載!*博主的文章是自己平時開發總結的經驗,由于博主的水平不高,不足和錯誤之處在所難免,希望大家能夠批評指出,
*我的博客: http://www.cnblogs.com/lxhbky/
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/445838.html
標籤:.NET Core
上一篇:Net6 Configuration & Options 原始碼分析 Part2 Options 模型使用與原始碼分析
下一篇:Net6 Configuration & Options 原始碼分析 Part2 Options 模型使用與原始碼分析
