为什么设置RequestInitializationTimeout属性?

D.R*_*.R. 13 .net wcf

什么是HttpTransportElement的RequestInitializationTimeout?MSDN说:

获取或设置时间跨度,该时间跨度指定在超时之前请求初始化必须完成的时间.

究竟什么是"请求初始化"?为什么我要设置此属性?

Chr*_*ras 7

没有关于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.
Run Code Online (Sandbox Code Playgroud)

如果我们检查TimeoutException它分配给与内部requestException一起传递HttpRequestContext.SetMessage(message, requestException)OnParseComplete,我们可以看到:

// 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.
Run Code Online (Sandbox Code Playgroud)

并且关于异常消息是:SR.RequestInitializationTimeoutReached System.Private.ServiceModel

The initialization process of the request message timed out after {0}. To increase this quota, use the '{1}' property on the '{2}'.
Run Code Online (Sandbox Code Playgroud)

这将被格式化为这样的:

The initialization process of the request message timed out after 00:00:10. To increase this quota, use the 'RequestInitializationTimeout' property on the 'HttpTransportBindingElement'.
Run Code Online (Sandbox Code Playgroud)

tl;博士

如果我们按照源代码,我们可以在里面找到它的用法EmptyHttpPipeline

承建商 EmptyHttpPipeline.EmptyHttpPipeline

如果有一个RequestInitializationTimeout TimeSpan而不是默认00:00:00禁用它),一个IOThreadTimer设置与dueTime设置到TimeSpan蜱,在回调(实际对象)作为。OnRequestInitializationTimeout ActionthisEmptyHttpPipelinecallbackState

new TimeoutException(SR.GetString(
    SR.RequestInitializationTimeoutReached,
    this.HttpRequestContext.Listener.RequestInitializationTimeout,
    "RequestInitializationTimeout",
    typeof(HttpTransportBindingElement).Name))
Run Code Online (Sandbox Code Playgroud)

什么时候查的?

当它的检查EmptyHttpPipeline.OnParseComplete方法是由试图取消被叫requestInitializationTimer。如果有一个计时器并且它之前被取消或过期,它会创建一个TimeoutException它分配给requestException它的 ,它与HttpPipeline.HttpRequestContext.SetMessage一起传递给message

The initialization process of the request message timed out after {0}. To increase this quota, use the '{1}' property on the '{2}'.
Run Code Online (Sandbox Code Playgroud)

OnParseComplete方法被称为内EnqueueMessageAsyncResult.CompleteParseAndEnqueue使用内部的方法。该调用时被创建对象和它终止内部。HandleParseIncomingMessage AsyncCallbackEnqueueMessageAsyncResult.EndEnqueueMessageAsyncResultEmptyHttpPipeline.BeginProcessInboundRequestEmptyHttpPipeline.EndProcessInboundRequest

BeginProcessInboundRequestEndProcessInboundRequest

The initialization process of the request message timed out after 00:00:10. To increase this quota, use the 'RequestInitializationTimeout' property on the 'HttpTransportBindingElement'.
Run Code Online (Sandbox Code Playgroud)

EnqueueMessageAsyncResult.CompleteParseAndEnqueue

if (this.httpRequestContext.Listener.RequestInitializationTimeout != HttpTransportDefaults.RequestInitializationTimeout)
{
    this.requestInitializationTimer = new IOThreadTimer(onRequestInitializationTimeout, this, false);
    this.requestInitializationTimer.Set(this.httpRequestContext.Listener.RequestInitializationTimeout);
}
Run Code Online (Sandbox Code Playgroud)

它是如何被检查和取消的?

正在使用该CancelRequestInitializationTimer方法对其进行检查,如果没有设置计时器,则仅返回 true;如果它之前被取消,它返回false,否则它调用IOThreadTimer.Cancel方法来检查并返回true,如果它被取消,或者如果计时器过期则返回false 。

EmptyHttpPipeline.CancelRequestInitializationTimer

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);
}
Run Code Online (Sandbox Code Playgroud)

实际上被取消的是什么?

OnRequestInitializationTimeout方法内部,我们可以看到它取消了HttpPipeline中止的HttpPipeline.httpRequestContext

EmptyHttpPipeline.OnRequestInitializationTimeout

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();
}
Run Code Online (Sandbox Code Playgroud)

HttpPipeline.Cancel

void 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我们什么时候得到一个EmptyHttpPipeline对象?

EmptyHttpPipeline如果我们transportIntegrationHandler = null使用HttpPipeline.CreateHttpPipeline接受 aTransportIntegrationHandler并且没有的静态设置,我们会得到一个HttpRequestContext.HttpMessagesSupported

protected bool CancelRequestInitializationTimer()
{
    if (this.requestInitializationTimer == null)
    {
        return true;
    }

    if (this.requestInitializationTimerCancelled)
    {
        return false;
    }

    bool result = this.requestInitializationTimer.Cancel();
    this.requestInitializationTimerCancelled = true;

    return result;
}
Run Code Online (Sandbox Code Playgroud)

CreateHttpPipeline被称为上HttpRequestContext.InitializeHttpPipeline

HttpRequestContext.InitializeHttpPipeline

static void OnRequestInitializationTimeout(object obj)
{
    Fx.Assert(obj != null, "obj should not be null.");
    HttpPipeline thisPtr = (HttpPipeline)obj;
    thisPtr.Cancel();
}
Run Code Online (Sandbox Code Playgroud)

在使用内部调用HttpRequestContext requestContextHttpContextReceivedAsyncResult构造函数中传递HttpContextReceivedAsyncResult<TListenerChannel>.ProcessHttpContextAsyncHttpChannelListener<TListenerChannel>.transportIntegrationHandler

HttpContextReceivedAsyncResult<TListenerChannel>.ProcessHttpContextAsync

public virtual void Cancel()
{
  this.httpRequestContext.Abort();
}
Run Code Online (Sandbox Code Playgroud)

结论

所有这些都让我们得出结论,当 aHttpPipeline创建时没有 aTransportIntegrationHandler并且没有HttpMessagesSupportedfor HttpRequestContext,那么新EmptyHttpPipeline对象将为/设置超时(如果RequestInitializationTimeout设置), 并在使用inside设置消息时设置异常。BeginProcessInboundRequestEndProcessInboundRequestEmptyHttpPipeline.HttpRequestContext.SetMessageEmptyHttpPipeline.OnParseComplete