我在 ASP.NET Core 应用程序中遇到问题,其中
System.Text.Json.JsonSerializer.DeserializeAsync
在高负载下失败。当同时发出多个请求时,应用程序会抛出错误,并且响应速度会显着减慢。
错误信息
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.Text.Json.JsonException: The JSON value could not be converted to System.Collections.Generic.List`1[ServiceContract.Models.City]. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
at System.Text.Json.ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type propertyType)
at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize(ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
at CitiesService.CovidService.GetCities() in C:\Users\ahmed\RiderProjects\CitiesService\CovidService.cs:line 28
at HttpClientExample.Controllers.HomeController.Home() in C:\Users\ahmed\RiderProjects\HttpClientExample\Controllers\HomeController.cs:line 19
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execut
e(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g_
_Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awai
ted|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_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>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
解决方案结构
ClientService
- CovidService.cs
- CovidServiceSettings.cs
HttpClientExample (Main Project)
ServiceContract
- Models
- City.cs
- ICovidService.cs
代码
CovidService.cs
using System.Text.Json;
using ServiceContract.Models;
using ServiceContract;
namespace CitiesService;
public class CovidService : ICovidService
{
private readonly HttpClient _httpClient;
private readonly CovidServiceSettings _covidServiceSettings;
public CovidService(HttpClient httpClient, CovidServiceSettings covidServiceSettings)
{
_httpClient = httpClient;
_covidServiceSettings = covidServiceSettings;
}
public async Task<List<City>> GetCities()
{
HttpRequestMessage requestMessage = new()
{
RequestUri = new Uri(_covidServiceSettings.Url),
Method = HttpMethod.Get
};
HttpResponseMessage responseMessage = await _httpClient.SendAsync(requestMessage);
Stream stream = await responseMessage.Content.ReadAsStreamAsync();
List<City> cities = await JsonSerializer.DeserializeAsync<List<City>>(stream);
return cities;
}
}
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddHttpClient();
builder.Services.AddScoped<ICovidService, CovidService>(serviceProvider =>
{
var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
var configuration = serviceProvider.GetService<IConfiguration>();
return new CovidService(httpClientFactory!.CreateClient("CovidService"),
configuration.GetSection("CovidSettings").Get<CovidServiceSettings>());
});
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.MapControllers();
app.Run();
HomeController.cs
using Microsoft.AspNetCore.Mvc;
using ServiceContract;
namespace HttpClientExample.Controllers;
public class HomeController : Controller
{
private readonly ICovidService _covidService;
public HomeController(ICovidService covidService)
{
_covidService = covidService;
}
[Route("/")]
public async Task<IActionResult> Home()
{
var cities = await _covidService.GetCities();
return View(cities);
}
}
增加线程池限制:使用ThreadPool.SetMinThreads调整线程池,略微提高了高负载下的性能。该应用程序成功处理了前 20-30 个请求,但此后迅速降级。有时,在高负载期间会正确处理一两个额外请求,但总体而言瓶颈仍然存在。
我希望 JsonSerializer.DeserializeAsync 能够处理高并发请求,而不会出现明显的性能下降或失败。
在高负载期间,API 返回错误响应:
{"error":"API limit reached. Please try again later. Remaining Limit: 0"}
我没有在反序列化逻辑中考虑这些类型的错误响应,因此错误消息无法反序列化到我的
List<City>
模型中。
当我用硬编码的 JSON 数据替换 API 调用时,反序列化工作正常,确认问题是由于 缺少对意外响应的错误处理。