JsonSerializer.DeserializeAsync 在 ASP.NET Core 应用程序高负载下失败

问题描述 投票:0回答:1

我在 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 能够处理高并发请求,而不会出现明显的性能下降或失败。

performance asp.net-core .net-core async-await jsonserializer
1个回答
0
投票

在高负载期间,API 返回错误响应:

{"error":"API limit reached. Please try again later. Remaining Limit: 0"}

我没有在反序列化逻辑中考虑这些类型的错误响应,因此错误消息无法反序列化到我的

List<City>
模型中。

当我用硬编码的 JSON 数据替换 API 调用时,反序列化工作正常,确认问题是由于 缺少对意外响应的错误处理

© www.soinside.com 2019 - 2024. All rights reserved.