【问题标题】:Why set the RequestInitializationTimeout property?为什么要设置 RequestInitializationTimeout 属性?
【发布时间】:2015-06-17 19:40:12
【问题描述】:

HttpTransportElement 的 RequestInitializationTimeout 是关于什么的? MSDN 说:

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

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

【问题讨论】:

    标签: .net wcf


    【解决方案1】:

    没有关于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.
    

    如果我们检查它分配给requestExceptionTimeoutException,它与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'.
    

    tl;博士

    如果我们关注源代码,我们可以在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 AsyncCallbackEnqueueMessageAsyncResult.End 方法内部调用。 EnqueueMessageAsyncResult 对象在调用 EmptyHttpPipeline.BeginProcessInboundRequest 时创建,并在 EmptyHttpPipeline.EndProcessInboundRequest 内部终止。

    BeginProcessInboundRequestEndProcessInboundRequest

    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.CompleteParseAndEnqueue

    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);
        }
    }
    

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

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

    EmptyHttpPipeline.CancelRequestInitializationTimer

    protected 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.OnRequestInitializationTimeout

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

    HttpPipeline.Cancel

    public 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);
    }
    

    CreateHttpPipelineHttpRequestContext.InitializeHttpPipeline 上被调用:

    HttpRequestContext.InitializeHttpPipeline

    public void InitializeHttpPipeline(TransportIntegrationHandler transportIntegrationHandler)
    {
        this.httpPipeline = HttpPipeline.CreateHttpPipeline(this, transportIntegrationHandler, this.IsWebSocketRequest);
    }
    

    HttpContextReceivedAsyncResult<TListenerChannel>.ProcessHttpContextAsync 内使用HttpChannelListener<TListenerChannel>.transportIntegrationHandler 调用HttpRequestContext requestContext传入HttpContextReceivedAsyncResult 构造函数

    HttpContextReceivedAsyncResult<TListenerChannel>.ProcessHttpContextAsync

    AsyncCompletionResult ProcessHttpContextAsync()
    {
        bool abort = false;
        try
        {
            this.context.InitializeHttpPipeline(this.listener.transportIntegrationHandler);
            ...
        }
        ...
    }
    

    结论

    所有这些给我们的结论是,当创建一个 HttpPipeline 时没有 TransportIntegrationHandler 并且没有 HttpRequestContextHttpMessagesSupported,那么新的 EmptyHttpPipeline 对象将有一个超时(如果RequestInitializationTimeout 已设置)用于BeginProcessInboundRequest/EndProcessInboundRequest,并在使用EmptyHttpPipeline.HttpRequestContext.SetMessageEmptyHttpPipeline.OnParseComplete 内设置消息时设置异常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-19
      • 2021-02-22
      • 2019-05-23
      • 2011-03-23
      • 2023-03-12
      • 2011-07-24
      • 2010-11-10
      • 1970-01-01
      相关资源
      最近更新 更多