简体   繁体   English

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

[英]How can I make Named Pipe binding reconnect automatically in WCF

I'm writing a service that will only get calls from the local host. 我正在编写一个只接收来自本地主机的电话的服务。 Performance is important so I thought I'd try the NetNamedPipeBinding instead of NetTcpBinding and see If I could see any noticeable performance gains. 性能很重要,所以我想我会尝试NetNamedPipeBinding而不是NetTcpBinding ,看看我是否能看到任何明显的性能提升。

If a client, after having performed one or more requests to the server, is idle for a longer period of time the next request will fail seemingly due to some idle timeout in the binding. 如果客户端在对服务器执行了一个或多个请求之后,在较长时间内处于空闲状态,则下一个请求似乎会因绑定中的某些空闲超时而失败。 The same thing also happens when the service gets restarted. 服务重新启动时也会发生同样的事情。

I need my clients to be able to keep a connection open for as long as it's allowed in order to avoid the overhead associated with setting up a new connection. 我需要我的客户端能够在允许的情况下保持连接打开,以避免与设置新连接相关的开销。 I also need to be able to restart the service from time to time and have the clients to automatically retry if they notice that the connection has been terminated. 我还需要能够不时重新启动服务,并让客户端在发现连接已终止时自动重试。

I know that this is suppported by the reliability stuff in NetTcpBinding but how would one go about getting the same level of re-connect reliability in the NetNamedPipeBinding? 我知道这可以通过NetTcpBinding中的可靠性内容来支持,但是如何在NetNamedPipeBinding中获得相同级别的重新连接可靠性呢? Is it even possible? 它甚至可能吗?

The question is somewhat academic as it isn't a requirement to use NetNamedPipes, I could just as easily adopt it to use the tcp-binding but It's an itch and I'd really like to scratch it. 这个问题在某种程度上是学术性的,因为它不是使用NetNamedPipes的要求,我可以很容易地采用它来使用tcp绑定,但它是一个痒,我真的想抓它。

My experience is that, when using NetNamedPipes, the "ReceiveTimout" on the binding functions like an "Inactivity Timeout" rather than a receive timout. 我的经验是,当使用NetNamedPipes时,绑定上的“ReceiveTimout”功能就像“不活动超时”而不是接收时间。 Note that this different than how a NetTCPBinding works. 请注意,这与NetTCPBinding的工作方式不同。 With TCP, it really is a receive timeout and there's a separate inactivity timeout you can configure via reliable messaging. 使用TCP,它实际上是一个接收超时,并且您可以通过可靠的消息传递配置单独的非活动超时。 (It's also appears to be contrary to the SDK documentation, but oh well). (它似乎也与SDK文档相反,但很好)。

To fix this, set the RecieveTimout to something big when you create the binding. 要解决此问题,请在创建绑定时将RecieveTimout设置为较大的值。
For example, if you are creating your binding procedurally... 例如,如果您在程序上创建绑定...

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

Or, if you care creating your binding declaratively... 或者,如果您关心以声明方式创建绑定...

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

I haven't used NetNamedPipes in WCF but I spent more time than I cared to learning the timeout values for NetTcp. 我没有在WCF中使用过NetNamedPipes,但是我花了更多的时间来学习NetTcp的超时值。 I use the following configs for my NetTcpBindings and had good luck with the connection staying active. 我使用以下配置为我的NetTcpBindings,并祝好连接保持活跃。

Server: 服务器:

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

Client: 客户:

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

The important settings that I spent the most time on are the sendTimeout and receiveTimeout. 我花费最多时间的重要设置是sendTimeout和receiveTimeout。 If your receiveTimeout is the same or less than your send, the channel will drop once that timeout is reached. 如果您的receiveTimeout与您的发送相同或更少,则一旦达到超时,通道将会丢弃。 If the receive is higher and the send is above a threshold, the channel will fire a transport level keepalive. 如果接收较高且发送高于阈值,则通道将触发传输级别keepalive。 From my tests, the sendTimeout threshold is 30 seconds. 从我的测试中,sendTimeout阈值是30秒。 Anything less than that and the keepalives aren't sent. 任何低于此值的东西都不会被发送。

Additionally, I have a timer based keepalive call that I execute every minute to try and ensure the channel is up and working well. 此外,我有一个基于计时器的keepalive调用,我每分钟执行一次尝试,以确保通道正常运行。 The call is simply to a boolean return member: 该调用只是一个布尔返回成员:

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

public bool KeepAlive()
{
    return true;
}

You can also grab the channel events (if you get them at the right time) and reopen the connection if something bad happens: 您还可以获取频道事件(如果您在正确的时间获得它们)并在发生错误时重新打开连接:

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

I hope some of this translates and has value for you with NetNamedPipes. 我希望其中一些可以通过NetNamedPipes进行翻译并对您有价值。

Edit: More options for capturing the server restarted issue 编辑:捕获服务器重新启动问题的更多选项

When the server restarts it should cause the client's channel to either close or fault. 当服务器重新启动时,它应该导致客户端的通道关闭或出错。 Capturing those events on the client side would give you the option of using reconnect timer until the service is available again. 在客户端捕获这些事件将使您可以选择使用重新连接计时器,直到服务再次可用。

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

You would want to ensure the reconnect timer's login attempts are done on a background thread asynchronously so they don't hang the UI every time they attempt to login. 您可能希望确保重新连接计时器的登录尝试是在后台线程上异步完成的,这样每次尝试登录时它们都不会挂起UI。 YMMV 因人而异

I have been looking into the problem of dropped TCP connections for two days now and came to the conclusion that a lot of people are missing a crutial point when setting up connections in WCF. 我一直在研究连接两天的TCP连接丢失的问题,并得出结论,很多人在WCF中建立连接时错过了一个重要的观点。 What everybody seems to be doing is create a channel once and then trying to hold on to it for the lifetime of the application, playing all sorts of dirty tricks to keep the TCP session alive. 每个人似乎都在做的是创建一个通道,然后尝试在应用程序的生命周期中保持它,播放各种肮脏的技巧以保持TCP会话存活。 This is not how it was meant to be. 这不是它的意思。

You should create a channel, perform one (or some more shortly after the first) calls on your service, then close and dispose the channel. 您应该创建一个频道,在您的服务上执行一个(或在第一个之后不久)调用,然后关闭并处置该频道。 This will give you (virtually) stateless operation and you don't have to be bothered with keeping sessions alive, which you should not want in the first place. 这将给你(几乎)无状态操作,你不必为保持会话活着而烦恼,这是你不应该首先想要的。

You will find that the overhead of creating and closing a channel (from a reused ChannelFactory) is negligible, taking only some tens of nanoseconds on a typical machine. 您会发现创建和关闭通道(来自重用的ChannelFactory)的开销可以忽略不计,在典型的机器上只需要几十纳秒。

The receiveTimeout attribute that everyone is cranking up defines the time a channel can remain idle before it is automatically dropped, which tells you channels are not meant to be kept open for very long (the default is 1 minute). 每个人都在启动的receiveTimeout属性定义了一个频道在被自动删除之前可以保持空闲的时间,这告诉你频道不应该长时间保持打开(默认为1分钟)。 If you set receiveTimeout to TimeSpan.MaxValue it will keep your channel open longer but this is not what it is for nor what you want in a practical scenario. 如果将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. 最终让我走上正轨的是http://msdn.microsoft.com/en-us/library/ms734681.aspx ,它提供了一个可怕的错误示例,但确实展示了如何使用ChannelFactory。 Responders point out the bugs and set the record straight so all in all you can get everything you need here. 响应者指出错误并设置记录,所以总而言之,你可以在这里得到你需要的一切。

And then, all my problems were over. 然后,我所有的问题都结束了。 No more "An operation was attempted on something that is not a socket" and no more "An existing connection was forcibly closed by the remote host". 不再“尝试对不是套接字的东西进行操作”而不再“远程主机强行关闭现有连接”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM