如何在WCF中自动重命名命名管道绑定

时间:2008-12-04 13:09:21

标签: .net wcf wcf-binding

我正在编写一个只能从本地主机接听电话的服务。性能非常重要,所以我想我会尝试使用NetNamedPipeBinding代替NetTcpBinding,看看我是否能看到任何明显的性能提升。

如果客户端在对服务器执行了一个或多个请求之后,在较长时间内处于空闲状态,则下一个请求似乎会因绑定中的某些空闲超时而失败。服务重新启动时也会发生同样的事情。

我需要我的客户端能够在允许的情况下保持连接打开,以避免与设置新连接相关的开销。我还需要能够不时重新启动服务,并让客户端在发现连接已终止时自动重试。

我知道这可以通过NetTcpBinding中的可靠性内容来支持,但是如何在NetNamedPipeBinding中获得相同级别的重新连接可靠性呢?它甚至可能吗?

这个问题在某种程度上是学术性的,因为它不是使用NetNamedPipes的要求,我可以很容易地采用它来使用tcp绑定,但它是一个痒,我真的很想抓它。

3 个答案:

答案 0 :(得分:22)

我的经验是,当使用NetNamedPipes时,绑定上的“ReceiveTimout”功能就像“不活动超时”而不是接收时间。请注意,这与NetTCPBinding的工作方式不同。使用TCP,它确实是一个接收超时,你可以通过可靠的消息传递配置一个单独的非活动超时。 (它似乎与SDK文档相反,但是很好)。

要解决此问题,请在创建绑定时将RecieveTimout设置为较大的值 例如,如果您在程序上创建绑定...

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

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

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

答案 1 :(得分:17)

我没有在WCF中使用过NetNamedPipes,但是我花了更多的时间来学习NetTcp的超时值。我在NetTcpBindings中使用以下配置,并且在连接保持活动状态下运气良好。

服务器:

<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与您的发送相同或更少,则一旦达到超时,通道将会丢弃。如果接收较高且发送高于阈值,则通道将触发传输级别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;
    }
}

您可能希望确保重新连接计时器的登录尝试是在后台线程上异步完成的,因此每次尝试登录时都不会挂起UI。 YMMV

答案 2 :(得分:14)

我一直在研究已经连续两天丢弃TCP连接的问题,并得出结论,在WCF中建立连接时,很多人都错过了一个重要的观点。每个人似乎都在做的是创建一个通道,然后尝试在应用程序的生命周期中保持它,播放各种肮脏的技巧以保持TCP会话存活。这不是它的意思。

您应该创建一个频道,在您的服务上执行一个(或在第一个之后不久),然后关闭并处置该频道。这将给你(几乎)无状态操作,你不必为保持会话活着而烦恼,这是你不应该首先想要的。

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

每个人都在启动的receiveTimeout属性定义了一个频道在自动删除之前保持空闲的时间,这告诉你频道不能长时间保持打开(默认为1分钟)。如果将receiveTimeout设置为TimeSpan.MaxValue,它将使您的频道保持更长时间,但这不是它的用途,也不是您在实际场景中想要的。

最终让我走上正轨的是 http://msdn.microsoft.com/en-us/library/ms734681.aspx 它提供了一个可怕的错误示例,但确实展示了如何使用ChannelFactory。响应者指出了错误并将记录设置为正确,所以总而言之,您可以获得所需的一切。

然后,我所有的问题都结束了。没有更多“尝试对不是套接字的东西进行操作”而且没有“远程主机强行关闭现有连接”。