Net Core - почему промежуточное ПО исключений так медленно? - PullRequest
0 голосов
/ 20 июня 2019

Прежде всего, извините за мой английский.

Ну, я разрабатываю .Net Core Rest Api (я разрабатывал это раньше для asp.net mvc, но в net core - впервые), и я столкнулся со странной вещью.

Приложение разработано в различных слоях: WebApi -> Core <- Инфраструктура. </p>

Дело в том, что в службах основного уровня я решил использовать некоторые исключения для некоторых бизнес-правил. Например, есть некоторые переводимые сущности, когда вы хотите создать новую сущность, вы должны предоставить перевод для каждой культуры, определенной в приложении. Если пропущен перевод, я выбрасываю исключение.

Пример метода:

public async Task<CompanyResource> Add(CompanyResource companyResource)
    {
        var cultures = await _cultureRepository.GetAllAsync();
        if (cultures.Any(culture => companyResource.Translations.All(t => t.CultureCode != culture.Code)))
            throw new MissingTranslationsException();

        await _companyResourceRepository.AddAsync(companyResource);

        return companyResource;
    }

Теперь мне было интересно, как обрабатывать исключения в контроллере для отправки клиенту правильного httpStatusCode в зависимости от каждого типа исключения и удобочитаемого сообщения.

В поисках я нашел 3 основных способа: - Попробуй / поймай в методе в контроллере - Пользовательский фильтр исключений - Пользовательское исключение Midleware

Я решил использовать промежуточное ПО, так как это, кажется, лучший способ - иметь центрированное место во всем приложении для обработки исключений и отправки сообщений клиентам, поэтому я сделал эту реализацию:

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate _next;

    public ErrorHandlingMiddleware(RequestDelegate next)
    {
        this._next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {

        var code = HttpStatusCode.InternalServerError;

        if (exception is InvalidPermissionsException) code = HttpStatusCode.Forbidden;
        else if (exception is DuplicateEntityException) code = HttpStatusCode.Conflict;
        else if (exception is MissingTranslationsException) code = HttpStatusCode.BadRequest;
        else if (exception is ApplicationException) code = HttpStatusCode.BadRequest;

        var result = JsonConvert.SerializeObject(new []{ exception.Message });
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)code;
        await context.Response.WriteAsync(result);

    }

}

Затем я подключил промежуточное ПО к конвейеру:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {

        //...other code

        app.UseMiddleware<ErrorHandlingMiddleware>();

        app.UseHttpsRedirection();

        app.UseMvc();

    }

Ну, в этот момент все работало отлично, buuuuuut, я просто заметил, что время отклика далеко не приемлемо, когда исключения обрабатываются промежуточным ПО.

Время отклика при использовании промежуточного программного обеспечения:

enter image description here

Проверено несколько раз, и среднее время ответа составляет 460 мс + -.

Ну, я твердо решил, что должен быть способ идти быстрее, а затем я попробовал другие 2 метода. За исключением атрибута фильтра:

public class HandleExceptionAttribute : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        var code = HttpStatusCode.InternalServerError;
        var exception = context.Exception;

        if (exception is InvalidPermissionsException) code = HttpStatusCode.Forbidden;
        else if (exception is DuplicateEntityException) code = HttpStatusCode.Conflict;
        else if (exception is MissingTranslationsException) code = HttpStatusCode.BadRequest;
        else if (exception is ApplicationException) code = HttpStatusCode.BadRequest;

        context.HttpContext.Response.StatusCode = (int)code;
        context.Result = new JsonResult(new[] { exception.Message });

    }
}

А при использовании фильтра время отклика уменьшилось до 250 мс + - в среднем. Я все еще крутой, это слишком медленно .. Поэтому я выбрал последний вариант: использование try catch внутри метода действия:

[HttpPost]     
    public async Task<IActionResult> Post([FromBody] CompanyResourceDto companyResourceDto)
    {

        if (!ModelState.IsValid) return BadRequest(ModelState);
        var companyresource = _mapper.Map<CompanyResource>(companyResourceDto);

        try
        {
            companyresource = await _resourcesService.Add(companyresource);
        }
        catch (Exception e)
        {
            return BadRequest(new[] {e.Message});
        }     

        return CreatedAtRoute("Getresource", new { id = companyresource.Id }, _mapper.Map<CompanyResourceDto>(companyresource));
    }

И, черт возьми, среднее время отклика в случае исключения уменьшилось до 85 мс + - (который я исключал с самого начала, с промежуточным программным обеспечением).

Честно говоря, я считаю, что для обеспечения безопасности и разделения интересов лучше всего использовать промежуточное программное обеспечение, потому что я не хочу, чтобы мои контроллеры знали о чем-либо, кроме нормального потока ..

Итак, мои вопросы:

Я что-то не так делаю с промежуточным ПО? Если нет, то почему так медленно и какой подход я должен принять, принимая во внимание, что время отклика имеет решающее значение в этом приложении? Если да, что я должен изменить?

Спасибо и всего наилучшего,

1 Ответ

0 голосов
/ 21 июня 2019

После некоторых тестов я не нашел способа продемонстрировать это, но, похоже, проблема в режиме отладки на локальной машине и перегреве производительности трассировки стека, как предложил Роберт Перри в комментариях.

После настройкиAPI в производстве на свежем сервере, промежуточное ПО перехватывает все исключения в среднем за 15 мс + -.

Я опубликую дополнительную информацию здесь, если узнаю, почему время отклика в производственном режиме на моем локальном компьютере не является лучшим (возможно, оно должно что-то делать с переменной enviroment, но я не уверен)

В любом случае, спасибо всем.

С уважением

...