【发布时间】:2015-06-17 19:40:12
【问题描述】:
HttpTransportElement 的 RequestInitializationTimeout 是关于什么的? MSDN 说:
获取或设置时间跨度,该时间跨度指定请求初始化在超时之前必须完成的时间。
究竟什么是“请求初始化”?为什么我要设置此属性?
【问题讨论】:
HttpTransportElement 的 RequestInitializationTimeout 是关于什么的? MSDN 说:
获取或设置时间跨度,该时间跨度指定请求初始化在超时之前必须完成的时间。
究竟什么是“请求初始化”?为什么我要设置此属性?
【问题讨论】:
没有关于RequestInitializationTimeout的明确文档。
SharedHttpTransportManager.IsCompatible的源码里面有一条关于RequestInitializationTimeout检查的注释:
// We are NOT checking the RequestInitializationTimeout here since the HttpChannelListener should be handle them // individually. However, some of the scenarios might be impacted, e.g., if we have one endpoint with high RequestInitializationTimeout // and the other is just normal, the first endpoint might be occupying all the receiving loops, then the requests to the normal endpoint // will experience timeout issues. The mitigation for this issue is that customers should be able to increase the MaxPendingAccepts number.
如果我们检查它分配给requestException 的TimeoutException,它与OnParseComplete 内的HttpRequestContext.SetMessage(message, requestException) 一起传递,我们可以看到:
new TimeoutException(SR.GetString(
SR.RequestInitializationTimeoutReached,
this.HttpRequestContext.Listener.RequestInitializationTimeout,
"RequestInitializationTimeout",
typeof(HttpTransportBindingElement).Name))
关于SR.RequestInitializationTimeoutReachedSystem.Private.ServiceModel的异常消息是:
The initialization process of the request message timed out after {0}. To increase this quota, use the '{1}' property on the '{2}'.
这将被格式化为这样的:
The initialization process of the request message timed out after 00:00:10. To increase this quota, use the 'RequestInitializationTimeout' property on the 'HttpTransportBindingElement'.
如果我们关注源代码,我们可以在EmptyHttpPipeline中找到它的用法。
EmptyHttpPipeline.EmptyHttpPipeline
如果有RequestInitializationTimeoutTimeSpan(而不是禁用它的default 00:00:00),则设置IOThreadTimer,并将dueTime设置为TimeSpan刻度,OnRequestInitializationTimeoutAction 回调和this(实际的EmptyHttpPipeline 对象)作为callbackState。
if (this.httpRequestContext.Listener.RequestInitializationTimeout != HttpTransportDefaults.RequestInitializationTimeout)
{
this.requestInitializationTimer = new IOThreadTimer(onRequestInitializationTimeout, this, false);
this.requestInitializationTimer.Set(this.httpRequestContext.Listener.RequestInitializationTimeout);
}
通过尝试取消requestInitializationTimer 来检查EmptyHttpPipeline.OnParseComplete 方法何时被调用。如果有一个计时器并且它之前被取消或过期,它会创建一个TimeoutException,并将其分配给requestException,并将其与message一起传递给HttpPipeline.HttpRequestContext.SetMessage:
protected override void OnParseComplete(Message message, Exception requestException)
{
if (!this.CancelRequestInitializationTimer() && requestException == null)
{
requestException = FxTrace.Exception.AsError(new TimeoutException(SR.GetString(
SR.RequestInitializationTimeoutReached,
this.HttpRequestContext.Listener.RequestInitializationTimeout,
"RequestInitializationTimeout",
typeof(HttpTransportBindingElement).Name)));
}
this.HttpRequestContext.SetMessage(message, requestException);
}
OnParseComplete 方法在 EnqueueMessageAsyncResult.CompleteParseAndEnqueue 内部使用 HandleParseIncomingMessage AsyncCallback 在 EnqueueMessageAsyncResult.End 方法内部调用。 EnqueueMessageAsyncResult 对象在调用 EmptyHttpPipeline.BeginProcessInboundRequest 时创建,并在 EmptyHttpPipeline.EndProcessInboundRequest 内部终止。
BeginProcessInboundRequest 和 EndProcessInboundRequest
internal override IAsyncResult BeginProcessInboundRequest(
ReplyChannelAcceptor replyChannelAcceptor,
Action dequeuedCallback,
AsyncCallback callback,
object state)
{
this.TraceBeginProcessInboundRequestStart();
return new EnqueueMessageAsyncResult(replyChannelAcceptor, dequeuedCallback, this, callback, state);
}
internal override void EndProcessInboundRequest(IAsyncResult result)
{
// It will trigger HandleParseIncomingMessage AsyncCallback,
// that will call CompleteParseAndEnqueue
EnqueueMessageAsyncResult.End(result);
this.TraceProcessInboundRequestStop();
}
EnqueueMessageAsyncResult.CompleteParseAndEnqueuevoid CompleteParseAndEnqueue(IAsyncResult result)
{
using (DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.BoundOperation(this.CallbackActivity) : null)
{
Exception requestException;
Message message = this.pipeline.EndParseIncomingMesssage(result, out requestException);
if ((message == null) && (requestException == null))
{
throw FxTrace.Exception.AsError(
new ProtocolException(
SR.GetString(SR.MessageXmlProtocolError),
new XmlException(SR.GetString(SR.MessageIsEmpty))));
}
// Here the EmptyHttpPipeline.OnParseComplete is being invoked
this.pipeline.OnParseComplete(message, requestException);
this.acceptor.Enqueue(this.pipeline.HttpRequestContext, this.dequeuedCallback, true);
}
}
它正在使用CancelRequestInitializationTimer 方法进行检查,如果没有设置计时器,它只返回true;如果之前取消,则返回 false,否则调用IOThreadTimer.Cancel 方法检查,如果取消则返回 true,如果计时器过期则返回 false。
EmptyHttpPipeline.CancelRequestInitializationTimerprotected bool CancelRequestInitializationTimer()
{
if (this.requestInitializationTimer == null)
{
return true;
}
if (this.requestInitializationTimerCancelled)
{
return false;
}
bool result = this.requestInitializationTimer.Cancel();
this.requestInitializationTimerCancelled = true;
return result;
}
在OnRequestInitializationTimeout 方法中,我们可以看到它取消了HttpPipeline,从而中止了HttpPipeline.httpRequestContext:
EmptyHttpPipeline.OnRequestInitializationTimeoutstatic void OnRequestInitializationTimeout(object obj)
{
Fx.Assert(obj != null, "obj should not be null.");
HttpPipeline thisPtr = (HttpPipeline)obj;
thisPtr.Cancel();
}
HttpPipeline.Cancelpublic virtual void Cancel()
{
this.httpRequestContext.Abort();
}
EmptyHttpPipeline 对象?如果我们使用接受TransportIntegrationHandler 且没有HttpRequestContext.HttpMessagesSupported 的静态HttpPipeline.CreateHttpPipeline 设置transportIntegrationHandler = null,我们将得到EmptyHttpPipeline:
public static HttpPipeline CreateHttpPipeline(HttpRequestContext httpRequestContext, TransportIntegrationHandler transportIntegrationHandler, bool isWebSocketTransport)
{
if (transportIntegrationHandler == null)
{
Fx.Assert(!isWebSocketTransport, "isWebSocketTransport should be false if there's no HTTP message handler existing.");
if (httpRequestContext.HttpMessagesSupported)
{
return new HttpMessageSupportedHttpPipeline(httpRequestContext);
}
return new EmptyHttpPipeline(httpRequestContext);
}
return NormalHttpPipeline.CreatePipeline(httpRequestContext, transportIntegrationHandler, isWebSocketTransport);
}
CreateHttpPipeline 在 HttpRequestContext.InitializeHttpPipeline 上被调用:
HttpRequestContext.InitializeHttpPipelinepublic void InitializeHttpPipeline(TransportIntegrationHandler transportIntegrationHandler)
{
this.httpPipeline = HttpPipeline.CreateHttpPipeline(this, transportIntegrationHandler, this.IsWebSocketRequest);
}
在HttpContextReceivedAsyncResult<TListenerChannel>.ProcessHttpContextAsync 内使用HttpChannelListener<TListenerChannel>.transportIntegrationHandler 调用HttpRequestContext requestContext(传入HttpContextReceivedAsyncResult 构造函数)
HttpContextReceivedAsyncResult<TListenerChannel>.ProcessHttpContextAsyncAsyncCompletionResult ProcessHttpContextAsync()
{
bool abort = false;
try
{
this.context.InitializeHttpPipeline(this.listener.transportIntegrationHandler);
...
}
...
}
所有这些给我们的结论是,当创建一个 HttpPipeline 时没有 TransportIntegrationHandler 并且没有 HttpRequestContext 的 HttpMessagesSupported,那么新的 EmptyHttpPipeline 对象将有一个超时(如果RequestInitializationTimeout 已设置)用于BeginProcessInboundRequest/EndProcessInboundRequest,并在使用EmptyHttpPipeline.HttpRequestContext.SetMessage 在EmptyHttpPipeline.OnParseComplete 内设置消息时设置异常。
【讨论】: