如何在服务器端有效限制请求长度超时?我使用Microsoft.Owin.Host.HttpListener
并且有些情况(由于呼叫外部服务)服务请求需要花费大量时间。这不是问题 - 但是网络服务器应该早点放弃 - 永远不会(我做了一些测试,但是在5分钟后我停止了它)。
有没有办法限制服务单个请求的时间(类似于IIS生态系统中的<httpRuntime maxRequestLength="..." />
)?
示例控制器代码:
public async Task<HttpResponseMessage> Get() {
// ... calls to 3pty services here
await Task.Delay(TimeSpan.FromMinutes(5));
}
启动网络服务器:
WebApp.Start(this.listeningAddress, new Action<IAppBuilder>(this.Build));
注意:我已经阅读了有关限制http侦听器的内容,但这只是限制了传入的请求属性,它不会取消因服务器处理速度慢而导致请求缓慢的请求:
var listener = appBuilder.Properties[typeof(OwinHttpListener).FullName] as OwinHttpListener;
var timeoutManager = listener.Listener.TimeoutManager;
timeoutManager.DrainEntityBody = TimeSpan.FromSeconds(20);
timeoutManager.EntityBody = TimeSpan.FromSeconds(20);
timeoutManager.HeaderWait = TimeSpan.FromSeconds(20);
timeoutManager.IdleConnection = TimeSpan.FromSeconds(20);
timeoutManager.RequestQueue = TimeSpan.FromSeconds(20);
相关:
答案 0 :(得分:3)
从概念上讲,“较旧”的Web服务器解决方案 - 即IIS使用每个请求一个线程分离,ThreadAbortException
使用慢速请求。 Owin使用不同的哲学 - 即它根据请求激发新任务并且最好避免强行取消任务。这个问题有两个方面:
两者都可以使用中间件组件实现。对于客户端断开连接的情况(context.Request.CallCancelled
context
为IOwinContext
)
如果你只是想在很长时间内尽快取消服务器流程,我会推荐像
这样的东西。public class MyMiddlewareClass : OwinMiddleware
{
// 5 secs is ok for testing, you might want to increase this
const int WAIT_MAX_MS = 5000;
public MyMiddlewareClass(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
using (var source = CancellationTokenSource.CreateLinkedTokenSource(
context.Request.CallCancelled))
{
source.CancelAfter(WAIT_MAX_MS);
// combined "client disconnected" and "it takes too long" token
context.Set("RequestTerminated", source.Token);
await Next.Invoke(context);
}
}
}
然后在控制器中
public async Task<string> Get()
{
var context = this.Request.GetOwinContext();
var token = context.Get<CancellationToken>("RequestTerminated");
// simulate long async call
await Task.Delay(10000, token);
token.ThrowIfCancellationRequested();
return "Hello !";
}
避开客户端更复杂。中间件看起来像这样:
public static async Task ShutDownClientWhenItTakesTooLong(IOwinContext context,
CancellationToken timeoutToken)
{
await Task.Delay(WAIT_MAX_MS, timeoutToken);
if (timeoutToken.IsCancellationRequested)
{
return;
}
context.Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
}
public async Task ExecuteMainRequest(IOwinContext context,
CancellationTokenSource timeoutSource, Task timeoutTask)
{
try
{
await Next.Invoke(context);
}
finally
{
timeoutSource.Cancel();
await timeoutTask;
}
}
public override async Task Invoke(IOwinContext context)
{
using (var source = CancellationTokenSource.CreateLinkedTokenSource(
context.Request.CallCancelled))
using (var timeoutSource = new CancellationTokenSource())
{
source.CancelAfter(WAIT_MAX_MS);
context.Set("RequestTerminated", source.Token);
var timeoutTask = ShutDownClientWhenItTakesTooLong(context, timeoutSource.Token);
await Task.WhenAny(
timeoutTask,
ExecuteMainRequest(context, timeoutSource, timeoutTask)
);
}
}