ASP.NET Core如何处理请求超时问题?完整解决方案与最佳实践指南

在实际开发中,请求超时是影响系统稳定性和用户体验的重要因素之一。尤其是在调用外部接口、执行长时间任务或高并发场景下,如果没有合理的超时控制,很容易导致线程资源耗尽甚至系统崩溃。本文将系统讲解 ASP.NET Core 中处理请求超时的几种主流方式及最佳实践。

为什么需要处理请求超时?

ASP.NET Core 默认不会自动限制请求执行时间,因为不同业务场景对响应时间要求差异很大,例如文件下载、WebSocket、长轮询等 。但如果不加控制,会带来以下问题:

  • 长时间占用线程资源,影响并发能力
  • 外部依赖(数据库/API)异常导致请求堆积
  • 用户体验差(页面长时间无响应)
  • 容易触发网关或负载均衡超时(如 504)

因此,合理设置请求超时机制非常关键。

ASP.NET Core 内置超时中间件(推荐)

.NET 7+ 提供了官方的 RequestTimeouts Middleware,这是目前最标准的解决方案。

1. 启用中间件

builder.Services.AddRequestTimeouts();

var app = builder.Build();
app.UseRequestTimeouts();

注意:如果使用 UseRouting,必须在其之后调用 。

2. 单接口设置超时

app.MapGet("/test", async (HttpContext context) =>
{
    await Task.Delay(10000, context.RequestAborted);
    return "OK";
}).WithRequestTimeout(TimeSpan.FromSeconds(2));

或使用特性(Controller):

[RequestTimeout(2000)]
public IActionResult Get() { ... }

3. 全局超时配置

builder.Services.AddRequestTimeouts(options =>
{
    options.DefaultPolicy = new RequestTimeoutPolicy
    {
        Timeout = TimeSpan.FromSeconds(3)
    };
});

所有未单独配置的接口都会使用该默认超时策略。

4. 超时后的处理机制

当请求超时:

  • HttpContext.RequestAborted 会触发
  • CancellationToken.IsCancellationRequested = true
  • 默认返回 504 状态码(可自定义)

5. 自定义超时响应

options.DefaultPolicy = new RequestTimeoutPolicy
{
    Timeout = TimeSpan.FromSeconds(2),
    WriteTimeoutResponse = async context =>
    {
        context.Response.ContentType = "application/json";
        await context.Response.WriteAsync("{\"message\":\"Request Timeout\"}");
    }
};

CancellationToken 优雅终止任务(核心实践)

即使设置了超时,如果你的代码不响应取消信号,任务仍会继续执行。

正确写法:

public async Task<IActionResult> GetData(HttpContext context)
{
    try
    {
        await Task.Delay(10000, context.RequestAborted);
    }
    catch (TaskCanceledException)
    {
        return StatusCode(408, "Request canceled");
    }

    return Ok();
}

关键点:

  • 使用 context.RequestAborted
  • 所有 I/O 操作(数据库、HTTP请求)都要传入 token

Kestrel 与网关层超时(补充)

除了应用层,服务器层也会影响超时:

1. Kestrel 限制

Kestrel 提供一些连接级别限制(如 KeepAlive、HeadersTimeout),但无法强制终止正在执行的业务代码。

2. 反向代理 / 网关

常见如:

  • Nginx
  • Azure App Gateway
  • AWS ALB

这些通常会在固定时间后直接返回504 Gateway Timeout。

注意:此时 ASP.NET Core 仍可能继续执行任务(必须结合 CancellationToken)。

禁用或动态控制超时

1. 禁用某接口超时

app.MapGet("/longtask", [DisableRequestTimeout] async () =>
{
    await Task.Delay(10000);
});

2. 动态取消超时

var feature = context.Features.Get<IHttpRequestTimeoutFeature>();
feature?.DisableTimeout();

适用于:

  • 文件上传
  • 长任务处理
  • 流式响应

处理请求超时最佳实践

在生产环境中,建议采用组合策略:

1. 中间件控制整体超时

  • 设置全局默认超时(3~10秒)
  • 特殊接口单独放宽

2. 所有异步操作支持 CancellationToken

  • 数据库查询
  • HttpClient 请求
  • 外部服务调用

3. 分层控制

  • 应用层:RequestTimeout Middleware
  • 服务层:CancellationToken
  • 网关层:Nginx/负载均衡超时

4. 避免长阻塞操作

  • 使用 async/await
  • 避免同步 I/O

总结

ASP.NET Core 并不会自动帮你杀死超时请求,而是通过:

  • 中间件触发超时
  • CancellationToken 通知任务终止
  • 应用自行决定如何处理

这也意味着真正的超时控制能力,掌握在开发者手中。

评论