English 中文(简体)
我该如何在WCF中使命名管道绑定自动重新连接?
原标题:
  • 时间:2008-12-04 13:09:21
  •  标签:

我正在编写一个只接受本地主机调用的服务。性能很重要,所以我想尝试使用NetNamedPipeBinding而不是NetTcpBinding,看看是否可以获得明显的性能提升。

如果客户端在向服务器发送一个或多个请求后处于闲置状态较长时间,则下一个请求将因绑定中的空闲超时而失败。服务重新启动时也会发生同样的情况。

我需要我的客户能够保持连接开放,尽可能长的时间,以避免与建立新连接相关的开销。我也需要能够从时间重新启动服务,如果客户注意到连接已终止,他们需要自动重试。

我知道这是由NetTcpBinding中的可靠性机制支持的,但如何在NetNamedPipeBinding中获得同样的重新连接可靠性水平呢?这是否可能?

这个问题有点学术化,因为使用NetNamedPipes并不是一个必要的要求,我也可以很容易地采用tcp-binding来使用它,但是我想尝试一下NetNamedPipes。

问题回答

我的经验是,在使用NetNamedPipes时,绑定的“ReceiveTimout”函数就像是一个“非活动超时”,而不是一个真正的接收超时。注意,这与NetTCPBinding的工作方式不同。对于TCP,它确实是一个接收超时,并且还可以通过可靠的消息传递来配置一个单独的非活动超时。(这似乎与SDK文档相矛盾,但也无妨)。

To fix this, set the RecieveTimout to something big when you create the binding.
For example, if you are creating your binding procedurally...

NetNamedPipeBinding myBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
myBinding.ReceiveTimeout = TimeSpan.MaxValue;

或者,如果你关心以声明方式创建你的绑定...

<netNamedPipeBinding>
    <binding name="myBinding" receiveTimeout="infinite">
    </binding>
</netNamedPipeBinding>

我从未在WCF中使用NetNamedPipes,但我花了比我想象的更多的时间学习NetTcp的超时值。我使用以下配置我的NetTcpBindings,并且连接保持活动状态运行得非常好。

服务器 (fú wù qì)

<binding name="MyBindingName" sendTimeout="00:00:30" receiveTimeout="infinite">
    <reliableSession enabled="true" inactivityTimeout="00:05:00" ordered="true" />
    <security mode="None" />
</binding>

客户:

<binding name="MyBindingName" closeTimeout="00:00:30" openTimeout="00:00:30" receiveTimeout="infinite" sendTimeout="00:00:30">
    <reliableSession enabled="true" inactivityTimeout="00:01:00" ordered="true" />
    <security mode="None" />
</binding>

我花了最多时间调整的是sendTimeout和receiveTimeout这两个重要的设置。如果你的receiveTimeout相同或小于sendTimeout,那么一旦到达超时时间,通道将会断开。如果receiveTimeout更高且sendTimeout超过阈值,通道将会发出传输级别的keepalive。从我的测试来看,sendTimeout的阈值为30秒。少于这个时间的值不会发送keepalive。

此外,我有一个基于计时器的保持连接调用,我每分钟执行一次,以尝试确保通道正常运行。调用只是返回一个布尔成员:

[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = false)]
bool KeepAlive();

public bool KeepAlive()
{
    return true;
}

如果在正确的时间获取频道事件,您还可以抓取它们并在出现问题时重新打开连接:

InstanceContext site = new InstanceContext(this);
_proxy = new MyServiceChannel(site);
if (_proxy != null) 
{
    if (_proxy.Login()) 
    {
        //Login was successful
        //Add channel event handlers so we can determine if something goes wrong
        foreach (IChannel a in site.OutgoingChannels) 
        {
            a.Opened += Channel_Opened;
            a.Faulted += Channel_Faulted;
            a.Closing += Channel_Closing;
            a.Closed += Channel_Closed;
        }
    }
}

我希望这些对于您在NetNamedPipes方面有一定的翻译和价值。

编辑:捕获服务器重新启动问题的更多选项

当服务器重新启动时,应该导致客户端的通道关闭或出现故障。在客户端端捕获这些事件将使您有使用重新连接计时器直到服务再次可用的选项。

private void Channel_Faulted(object sender, EventArgs e)
{
    IChannel channel = sender as IChannel;
    if (channel != null) 
    {
        channel.Abort();
        channel.Close();
    }

    //Disable the keep alive timer now that the channel is faulted
    _keepAliveTimer.Stop();

    //The proxy channel should no longer be used
    AbortProxy();

    //Enable the try again timer and attempt to reconnect
    _reconnectTimer.Start();
}

private void _reconnectTimer_Tick(object sender, System.EventArgs e)
{
    if (_proxy == null) 
    {
        InstanceContext site = new InstanceContext(this);
        _proxy = new StateManagerClient(site);
    }
    if (_proxy != null) 
    {
        if (_proxy.Login()) 
        {
            //The connection is back up
            _reconnectTimer.Stop();
            _keepAliveTimer.Start();
        }
        else 
        {
            //The channel has likely faulted and the proxy should be destroyed
            AbortProxy();
        }
    }
}

public void AbortProxy()
{
    if (_proxy != null) 
    {
        _proxy.Abort();
        _proxy.Close();
        _proxy = null;
    }
}

您需要确保重连计时器的登录尝试是在后台线程异步完成的,以便每次尝试登录时不会阻塞用户界面。YMMV

我已经花了两天时间研究TCP连接丢失的问题,并得出结论,许多人在设置WCF连接时都忽略了一个关键点。每个人似乎都在创建一个通道后,试图将其保留到应用程序的生命周期中,并尝试使用各种不正当手段来保持TCP会话的活动状态。这不是它的本意。

你应该创建一个通道,在你的服务上执行一个(或几个)调用,然后关闭和处理通道。这将给你(实际上)无状态操作,你不必为保持活动会话而感到烦恼,而这也不是你希望的。

您会发现使用已重用的ChannelFactory来创建和关闭频道的开销是可以忽略不计的,在典型的机器上只需要几十纳秒。

每个人都在增加的 receiveTimeout 属性定义了通道可以保持空闲的时间,之后它将被自动丢弃,这告诉你通道不应该保持打开状态太长时间(默认为 1 分钟)。如果你将 receiveTimeout 设置为 TimeSpan.MaxValue,则可以使通道保持打开状态更长时间,但这不是它的实际用途或你在实际场景中所需的。

What finally got me on the right track was http://msdn.microsoft.com/en-us/library/ms734681.aspx which provides a horribly buggy example yet does show how one should go about using ChannelFactory. Responders point out the bugs and set the record straight so all in all you can get everything you need here.

然后,我所有的问题都解决了。没有更多的“尝试在非套接字上执行操作”和没有更多“现有连接被远程主机强制关闭”。





相关问题
热门标签