
今天带来了 .NET Core 的新文章 Timeout 中间件,让我们了解一下,看看我们可以实时应用哪些地方。
在实时应用程序(如金融交易平台)中,及时响应至关重要**。如果提供股票价格或执行交易的服务响应时间过长,可能会导致重大财务损失。 超时中间件可用于确保如果这些服务在指定时间范围内没有响应,则请求将中止,并向用户返回错误。**
了解 Timeout 中间件
默认情况下,ASP.NET Core 服务器不会执行此操作,因为请求处理时间因方案而异。例如,WebSockets、静态文件和调用昂贵的 API 都需要不同的超时限制。因此,ASP.NET Core 提供了配置每个终端节点的超时以及全局超时的中间件。
超时中间件在以下情况下特别有用:
有多种方法可以实现它。
一个。我们可以创建自定义 Middleware 来配置 Timeout
b.我们可以在 Controller 和 Action 级别使用 [RequestTimeout] 属性。
请考虑下面的代码
public classTimeoutClass
{
privatereadonlyRequestDelegate _next;
privatereadonlyTimeSpan _timeout;
publicTimeoutClass(RequestDelegate next,TimeSpan timeout)
{
_next = next;
_timeout = timeout;
}
publicasyncTaskInvokeAsync(HttpContext context)
{
using(var cts =newCancellationTokenSource(_timeout))
{
try
{
context.RequestAborted = cts.Token;
await_next(context);
}
catch(OperationCanceledException)
{
context.Response.StatusCode = StatusCodes.Status504GatewayTimeout;
await context.Response.WriteAsync("Request timed out happening.");
}
}
}
}
我们创建了 Timeout 类,并通过构造函数注入了 RequestDelegate 和超时时间。
在 Invoke Method 中,我们设置了指定超时的 Cancellation Token。当达到超时限制时,HttpContext.RequestAborted 中的 CancellationToken 会将 IsCancellationRequested 设置为。Abort() 不会在请求中自动调用,因此应用程序可能仍会生成成功或失败响应。如果应用程序不处理异常并生成响应,则默认行为是返回状态代码 504。true
注册 TimeoutClass 中间件
public staticclassTimeoutMiddlewareExtensions
{
publicstaticIApplicationBuilderUseTimeoutMiddleware(thisIApplicationBuilder builder,TimeSpan timeout)
{
return builder.UseMiddleware<TimeoutClass>(timeout);
}
}
程序
using Microsoft.AspNetCore.Http.Timeouts;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if(app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseTimeoutMiddleware(TimeSpan.FromSeconds());
app.UseAuthorization();
app.MapControllers();
app.Run();
KeyPoint:我们设置了 Timeout =10,表示如果任何请求耗时超过 10 秒,则会调用超时异常。
app.UseTimeoutMiddleware(TimeSpan.FromSeconds());
测试 TimeoutClass 中间件。
创建以下 API 端点
namespace TimeoutMiddleware.Controllers
{
[ApiController]
[Route("[controller]")]
publicclassDemoTimeOutConttoller:ControllerBase
{
[HttpGet("TestTimeOutMiddleware/{delay:int}")]
publicasyncTask<IActionResult>TestTimeOutMiddleware([FromRoute]int delay)
{
// in real time there will be long running http call or long running db call
await Task.Delay(TimeSpan.FromSeconds(delay), HttpContext.RequestAborted);
returnOk();
}
}
}
这里我们从用户输入中传递延迟时间。根据 Code ,我们预计如果延迟时间超过 10 秒,那么我们应该收到超时异常
让我们执行这段代码


我们按预期收到超时异常。
我们可以在控制器级别或操作级别使用 [RequestTimeout(“1000”)] 属性实现请求超时。
步骤
a. 使用以下代码更新程序.cs
通过调用 AddRequestTimeouts 将请求超时中间件添加到服务集合中。
通过调用 UseRequestTimeout 将中间件添加到请求处理管道。
using Microsoft.AspNetCore.Http.Timeouts;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddRequestTimeouts();
var app = builder.Build();
// Configure the HTTP request pipeline.
if(app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
//app.UseTimeoutMiddleware(TimeSpan.FromSeconds(10));
app.UseAuthorization();
app.UseRequestTimeouts();
app.MapControllers();
app.Run();
b. 在控制者级别申请。
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
[RequestTimeout("00:00:05")]// Timeout of 5 seconds
publicclassMyController:ControllerBase
{
[HttpGet("TimeOutFunction")]
publicasyncTask<IActionResult>TimeOutFunction()
{
// Simulate a long-running task like long running db call and http call
await Task.Delay();// 10 seconds
returnOk("Operation completed.");
}
}
说明:使用以下属性意味着我们已经在控制器级别配置了超时 =5 秒的所有端点,这意味着如果任何端点的超时时间超过 5 秒,将调用异常。
[RequestTimeout("00:00:05")] // Timeout of 5 seconds
c. 在操作级别
[ApiController]
[Route("api/[controller]")]
publicclassMyController:ControllerBase
{
[HttpGet("TwoSecondTimeout")]
[RequestTimeout("00:00:02")]// Timeout of 2 seconds
publicasyncTask<IActionResult>TwoSecondTimeout()
{
// Simulate a task that finishes quickly
await Task.Delay();// 1 second
returnOk("Done with work.");
}
[HttpGet("FiveSecondTimeout")]
[RequestTimeout("00:00:05")]// Timeout of 5 seconds
publicasyncTask<IActionResult>FiveSecondTimeout()
{
// Simulate a long-running task
await Task.Delay();// 10 seconds
returnOk("work completed.");
}
}
我们在操作级别应用了此属性,就像我们为每个端点配置了自己的超时期限一样。
对于最小的 API 应用程序,通过调用 WithRequestTimeout 或应用属性将终端节点配置为超时,如以下示例所示:[RequestTimeout]
using Microsoft.AspNetCore.Http.Timeouts;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRequestTimeouts();
var app = builder.Build();
app.UseRequestTimeouts();
//WAY 1
app.MapGet("/TwoSecondTimeout",async(HttpContext context)=>{
try
{
await Task.Delay(TimeSpan.FromSeconds(), context.RequestAborted);
}
catch(TaskCanceledException)
{
return Results.Content("Timeout!","text/plain");
}
return Results.Content("No timeout!","text/plain");
}).WithRequestTimeout(TimeSpan.FromSeconds());
// Returns "Timeout!"
//WAY 2 with Attribure
app.MapGet("/TwoSecondTimeout",
[RequestTimeout(milliseconds:)\]async(HttpContext context)=>{
try
{
await Task.Delay(TimeSpan.FromSeconds(), context.RequestAborted);
}
catch(TaskCanceledException)
{
return Results.Content("Timeout!","text/plain");
}
return Results.Content("No timeout!","text/plain");
});
// Returns "Timeout!"
app.Run();
假设我们在应用程序中有 10 个终端节点,并且我们想配置 4 个具有 5 秒超时的终端节点和 6 个具有 15 秒超时的终端节点,那么我们可以定义如下策略
builder.Services.AddRequestTimeouts(options =>{
options.DefaultPolicy =
newRequestTimeoutPolicy{ Timeout = TimeSpan.FromMilliseconds()};
options.AddPolicy("TwoSecondPolicy", TimeSpan.FromSeconds());
});app.MapGet("/namedpolicy",async(HttpContext context)=>{
try
{
await Task.Delay(TimeSpan.FromSeconds(), context.RequestAborted);
}
catch(TaskCanceledException)
{
return Results.Content("Timeout!","text/plain");
}
return Results.Content("No timeout!","text/plain");
}).WithRequestTimeout("TwoSecondPolicy");
// Returns "Timeout!"
应用程序中所有端点的全局超时设置
// Adding timeout middleware
app.Use(async(context, next)=>
{
var cancellationTokenSource =newCancellationTokenSource(TimeSpan.FromSeconds());
context.RequestAborted = cancellationTokenSource.Token;
try
{
awaitnext();
}
catch(OperationCanceledException)
{
context.Response.StatusCode = StatusCodes.Status408RequestTimeout;
}
});