12

ローカル ホストからの呼び出しのみを受け取るサービスを作成しています。パフォーマンスは重要なので、NetTcpBinding の代わりに NetNamedPipeBinding を試して、顕著パフォーマンスの向上が見られるかどうかを確認しようと考えました。

サーバーに対して 1 つ以上の要求を実行した後、クライアントが長時間アイドル状態の場合、バインディングのアイドル タイムアウトが原因で、次の要求が失敗するように見えます。サービスが再起動されたときも同じことが起こります。

新しい接続のセットアップに伴うオーバーヘッドを回避するために、クライアントが許可されている限り接続を開いたままにしておく必要があります。また、サービスを時々再起動し、クライアントが接続が終了したことに気付いた場合に自動的に再試行できるようにする必要もあります。

これが NetTcpBinding の信頼性によってサポートされていることは知っていますが、NetNamedPipeBinding で同じレベルの再接続の信頼性を得るにはどうすればよいでしょうか? それは可能ですか?

NetNamedPipes を使用する必要がないため、質問はやや学術的です。tcp バインディングを使用するためにそれを簡単に採用できますが、それはかゆいので、本当にスクラッチしたいと思います。

4

3 に答える 3

23

私の経験では、NetNamedPipes を使用する場合、バインディングの「ReceiveTimout」は、受信タイムアウトではなく「非アクティブ タイムアウト」のように機能します。これは、NetTCPBinding の動作とは異なることに注意してください。TCP では、これは実際には受信タイムアウトであり、信頼できるメッセージングを介して構成できる別の非アクティブ タイムアウトがあります。(SDK のドキュメントに反しているようにも見えますが、まあまあです)。

これを修正するには、バインディングを作成するときに RecieveTimout を大きな値に設定します。
たとえば、プロシージャルにバインディングを作成している場合...

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

または、バインディングを宣言的に作成したい場合...

<netNamedPipeBinding>
    <binding name="myBinding" receiveTimeout="infinite">
    </binding>
</netNamedPipeBinding>
于 2011-01-05T21:55:52.917 に答える
18

私は 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 が send と同じかそれ以下の場合、そのタイムアウトに達するとチャネルはドロップします。受信がより高く、送信がしきい値を超えている場合、チャネルはトランスポート レベルのキープアライブを起動します。私のテストでは、sendTimeout のしきい値は 30 秒です。それ未満の場合、キープアライブは送信されません。

さらに、タイマー ベースのキープアライブ コールを毎分実行して、チャネルが正常に動作していることを確認しています。呼び出しは単純にブール値の戻り値のメンバーに対して行われます:

[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

于 2008-12-04T15:01:37.453 に答える
14

TCP 接続が切断される問題を 2 日間調査してきましたが、多くの人が WCF で接続をセットアップする際に重要なポイントを見逃しているという結論に達しました。誰もがやっているように見えるのは、一度チャネルを作成してから、アプリケーションの存続期間中それを保持しようとし、TCP セッションを維持するためにあらゆる種類の汚いトリックを実行することです。これは意図された方法ではありません。

チャネルを作成し、サービスで 1 つ (または最初の呼び出しの直後に複数回) の呼び出しを実行してから、チャネルを閉じて破棄する必要があります。これにより、(事実上) ステートレスな操作が可能になり、そもそもセッションを維持する必要がなくなります。

(再利用された ChannelFactory からの) チャネルの作成とクローズのオーバーヘッドはごくわずかであり、一般的なマシンでは数十ナノ秒しかかからないことがわかります。

誰もがクランクアップしている receiveTimeout 属性は、チャネルが自動的にドロップされる前にアイドル状態を維持できる時間を定義します。これは、チャネルが非常に長い間開いたままにしておくことを意図していないことを示しています (デフォルトは 1 分です)。receiveTimeout を TimeSpan.MaxValue に設定すると、チャネルをより長く開いたままにできますが、これは目的でもなく、実際のシナリオで必要なことでもありません。

最終的に私を正しい軌道に乗せたのは http://msdn.microsoft.com/en-us/library/ms734681.aspx で、これはひどくバグのある例を提供しますが、ChannelFactory を使用する方法を示しています。レスポンダーはバグを指摘し、記録を正すので、必要なものはすべてここで入手できます.

そして、私の問題はすべて終わりました。「ソケットではないものに対して操作が試行されました」や「既存の接続がリモートホストによって強制的に閉じられました」ということはもうありません。

于 2010-01-14T16:30:38.680 に答える