Net Core-为什么异常中间件这么慢?

时间:2019-06-20 10:08:09

标签: c# .net-core asp.net-core-webapi

首先,对不起我的英语。

好吧,我正在开发.Net Core Rest Api(我以前在asp.net mvc中开发过,但是第一次在net core中开发),我只是遇到了一件奇怪的事情。

该应用程序在各个层次上开发:WebApi->核心<-基础结构。

重点是,在核心层的服务中,我决定对某些业务规则进行一些例外处理。例如,有一些可翻译的实体,当您要创建新实体时,必须为应用程序中定义的每种区域性提供翻译。如果缺少翻译,我会抛出异常。

方法示例:

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发送给客户端。

四处搜寻,发现了三种主要方法: -尝试/捕捉控制器中的方法 -自定义例外过滤器 -自定义例外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

经过多次测试,平均响应时间为460ms +-。

好吧,我坚信必须要有一种更快的方法,然后我尝试了另外两种方法。带有exceptionFilter属性:

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 });

    }
}

使用过滤器时,响应时间下降到平均250ms +-。 我仍然很坚强,这太慢了。所以我选择了最后一个选择:在动作方法中使用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));
    }

而且,该死的情况下,发生异常时的平均响应时间降至85ms +-(我从一开始就使用中间件排除了该时间)。

老实说,对于可操作性和关注点分离,最好的方法是使用中间件,因为我不希望我的控制器知道正常流程之外的任何东西。

所以,我的问题是:

我是否对中间件做错了? 如果不是,为什么这么慢?考虑到响应时间对这个应用程序至关重要,我应该考虑哪种方法? 如果是,我该怎么更改?

感谢和问候,

1 个答案:

答案 0 :(得分:0)

经过一些测试,我还没有找到方法来演示它,但是似乎问题出在罗伯特·佩里(Robert Perry)在评论中建议的本地计算机上的调试模式和stacktrace性能过热。

在新服务器中将Api投入生产后,中间件软件平均在15毫秒内捕获所有异常。

如果我发现本地计算机在生产模式下的响应时间为何不是最好的,我将在此处发布更多信息(也许它必须对环境变量进行某些操作,但我不确定)

无论如何,谢谢大家。

致谢