WCF ChannelFactory和连接超时的最佳实践



我正在开发一个winform应用程序,它将作为windows服务访问自托管的WCF服务。我使用的是ChannelFactory而不是服务引用。我已经成功地连接并调用了WCF服务。问题是当我让应用程序闲置20分钟,然后再试着打另一个电话时。我收到以下错误:

" socket连接被终止。这可能是由于处理消息时出现错误、远程主机超过接收超时或底层网络资源问题造成的。本地套接字超时为'00:00:59.9489970'。"

我正在寻找管理连接的最佳实践。我目前已经创建了一个名为PrepareWCFConnection的函数(见下文),用于检查通道和ChannelFactory的状态。我在调用WCF服务之前调用这个方法。有更好的处理方式吗?

     public bool PrepareWCFConnection()
    {
        if ((channelFactory == null) || 
            (channelFactory.State == CommunicationState.Faulted) ||
            (channelFactory.State != CommunicationState.Opened))
        {
            channelFactory = new ChannelFactory<IService1>(new NetTcpBinding(), endpointAddress);
        }

        if ((proxy == null) ||
            (((IClientChannel)proxy).State == CommunicationState.Faulted) ||
            (((IClientChannel)proxy).State != CommunicationState.Opened))
        {
            proxy = channelFactory.CreateChannel(endpointAddress);
            ((IClientChannel)proxy).Open();
        }
        return true;
    }

如果你想重用一个现有的通道,你需要通过每9分钟ping一次服务来保持通道的活动状态。我认为默认的接收超时是10分钟,所以如果通道保持空闲状态超过这个时间,通道将被断开连接。或者您可以使用可靠的会话来保持通道的活动。

如果您不需要在同一通道上回调,那么最好在完成后关闭该通道,并为每个服务操作重新创建一个新通道。创建通道的成本并不高。您可以缓存通道工厂,但是为每个调用创建通道。

我知道这个问题很老了,但我看到它并没有真正得到回答。当涉及到通道超时时,您应该关注两种超时(如果不使用可靠消息传递,则只有1种)。在服务端,您有"ReceiveTimeout",如果在超时期间内没有接收到应用程序消息,则触发它。此超时的默认值为10分钟。

还有"InactivityTimeout",仅在启用"ReliableSession"时使用。此超时是通道允许通信另一方在通道发生故障之前不发送任何消息的最大持续时间。

为了增加你的通道的生命周期,我建议你启用"ReliableSession",然后设置你的"ReceiveTimeout"&"InactivityTimeout"设置为更大的值。ReliableSession通过发送ILM(基础设施级消息)来保持通道的活动状态,例如keep-alive消息(也发送ack消息)。如果在"InactivityTimeout"过期之前未收到keep-alive或ALM(应用程序级消息),则通道将发生故障。

此外,如果在"ReceiveTimeout"过期之前未收到ALM(应用程序级消息),则通道将发生故障。

因此,建议将两个超时设置为相同的值,或者将"ReceiveTimeout"设置为比"InactivityTimeout"更高的值。

一个边注,设置"ReceiveTimeout"在客户端设置时没有作用,它只是服务端超时。但是当在服务端使用ReliableSession时,客户端也必须像这样实现它:

NetTcpBinding binding = new NetTcpBinding
        {
            ReliableSession = { Enabled = true },             
            SendTimeout = TimeSpan.FromMinutes( 1 )
        };
        binding.ReliableSession.InactivityTimeout = TimeSpan.Parse( "24.20:31:23.6470000" );

服务端的app.config应该是这样的:

<bindings>
    <netTcpBinding>
      <binding name="netTestTcpBinding"
               receiveTimeout="24.20:31:23.6470000">
        <reliableSession inactivityTimeout="24.20:31:23.6470000"
                         enabled="true" />
      </binding>
    </netTcpBinding>
  </bindings>
<services>
  <service>
    <endpoint address="IServiceContract"
              binding="netTcpBinding"
              bindingConfiguration="netTestTcpBinding"
              name="serviceContractTcpBinding"/>
    <host>
        <baseAddresses>
             <add baseAddress="net.tcp://localhost:12001/" />
        </baseAddresses>
     </host>
    </service>                   
</services>

一个非常直接的解决方案来重用你的通道,没有轮询或做花哨的东西,它只是照顾你的通道的最后一个调用,并检查wcfC.Binding.ReceiveTimeout以便在需要时重新生成它,像:

    TimeSpan timeSpan = DateTime.Now - LastCallTime;
    if (timeSpan.TotalSeconds > wcfC.Binding.ReceiveTimeout.TotalSeconds || wcfC.State != CommunicationState.Opened)
    { 
       wcfC.Abort();
       wcfC = new WCFChannel();
    }    
    LastCallTime = DateTime.Now;

最新更新