Asp.net核心中间件,如果花费很长时间,则取消请求

时间:2018-10-03 15:27:54

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

如果我的api花费的时间超过x的时间,我需要一个中间件来取消请求。 我所有的api调用都是可以取消的:

public async Task<IActionResult> Get(CancellationToken token) {  }

... 

public async Task InvokeAsync(HttpContext context)
{
    //....
    int customDurationMilis = 1000; //1second

    cancellTimer = new Timer(CancellRequestCallBack, context, customDurationMilis, Timeout.Infinite);
    //...

    await _next(context);
}

private void CancellRequestCallBack(HttpContext context)
{
    //log...
    //Aborts the connection underlying this request.
    context.Abort();
}

想象我有一个api操作方法需要2秒钟,计时器将触发 正在进行连接中止的CancellRequestCallBack。 有计时器是个好主意吗? 还是应该采用另一种方法? 如何返回自定义错误代码而不是context.Abort()并取消正在进行的api调用? 一些想法将不胜感激。 谢谢

更新,尝试这样做:

public async Task InvokeAsync(HttpContext context)
{
    var task = _next(context);

    if (await Task.WhenAny(task, Task.Delay(1000)) != task)
    {
        throw new MyCustomException("timeout!");
    }
}

至少,我在errorhandlermiddleware中捕获了返回状态代码500的异常。但是我没有在Postman中得到它。相反,我越来越 “无法得到任何回应” 而且我猜带有请求的线程仍将运行。

3 个答案:

答案 0 :(得分:3)

这实际上很难实现,原因有几个:

  • 取消是合作的。这意味着您需要(暂时)等待的所有事情都可以尊重令牌。
  • 保持异步状态意味着没有线程可以中止。您可以中止整个TCP连接,但是如果没有人在听令牌并且没有人在阅读正文,它将不会对正在运行的服务器代码产生任何作用。
  • 使用Task.WhenAny并不是一个好的解决方案,因为您可能最终会并行使用HttpContext。您“已取消”的代码可能即将在中间件尝试编写超时响应的同时编写一个有效的响应。即使抛出异常,也是很危险的,因为还有其他正在运行的代码可能并行触摸HttpContext

您可以冒险,但是可能存在细微的比赛条件,这些情况会导致事情以意外的方式失败。

如果您遵守取消令牌,则只需取消令牌并等待下一个撤销。完成后,您可以在写入响应之前查看响应是否已经开始,并返回408(请求超时)。

答案 1 :(得分:2)

提出了一种尊重令牌的解决方案。 这个想法是创建一个新令牌并为其定义超时。 但此外,还需要使用CreateLinkedTokenSource将原始令牌与新令牌链接。

public async Task InvokeAsync(HttpContext context)
{
    using (var timoutTS = CancellationTokenSource.CreateLinkedTokenSource(context.RequestAborted))
    {
        timoutTS.CancelAfter(200);
        context.RequestAborted = timoutTS.Token;
        await _next(context);
    }
}

答案 2 :(得分:0)

如何通过Polly添加弹性和瞬态故障处理?

Polly是一个.NET弹性和瞬态故障处理库,允许开发人员以流畅和线程安全的方式表达诸如重试,断路器,超时,隔离头和回退之类的策略。