2

WCFを使用してC#でクライアント/サーバーアプリケーションを作成しています。すべてのテストは正常に行われましたが、サービスを展開するとすぐに、サーバーとの通信にランダムな問題があることに気付きました。デバッグを有効にすると、サーバーで次のようなメッセージが表示されました。

The communication object, System.ServiceModel.Channels.ServerReliableDuplexSessionChannel, cannot be used for communication because it has been Aborted.

パターンは次のようになります。

  • クライアントがクエリを送信しています
  • サービスはクエリを処理しています
  • サービスは何かを送り返しています
  • アクティビティの境界は「停止」レベルです-すべてが正常に見えます
  • 信頼できるセッションのinactivityTimeoutを最後の連絡の日時に追加すると、サービスによってスローされた例外のタイムスタンプが得られます

アプリケーションは次のようになります。サービスインスタンスは、データベースと対話するためのAPIメソッドを提供し、タイプは「netTcpBinding」です。複数のクライアント(約40)が接続され、サービスからランダムにメソッドを呼び出します。クライアントは、何も送受信しなくても、数日間開いたままにすることができます。

関連するビットは次のとおりです。

サービス

    [ServiceContract(CallbackContract = typeof(ISVCCallback), SessionMode = SessionMode.Required)]
    [ExceptionMarshallingBehavior]
...

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext=true)]
    public class SVCService : ISVC
...

サービス構成

    <behaviors>
      <serviceBehaviors>
        <behavior name="behaviorConfig">
          <serviceMetadata httpGetEnabled="false" httpGetUrl="" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceThrottling maxConcurrentCalls="50" maxConcurrentSessions="1000"
            maxConcurrentInstances="50" />
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <bindings>
      <netTcpBinding>
        <binding name="tcpBinding" closeTimeout="00:01:00" openTimeout="00:10:00"
          receiveTimeout="23:59:59" sendTimeout="00:01:30" transferMode="Buffered"
          listenBacklog="1000" maxBufferPoolSize="671088640" maxBufferSize="671088640"
          maxConnections="1000" maxReceivedMessageSize="671088640"     portSharingEnabled="true">
          <readerQuotas maxStringContentLength="671088640" maxArrayLength="671088640"
            maxBytesPerRead="671088640" />
          <reliableSession inactivityTimeout="23:59:59" enabled="true" />
          <security mode="None">
          </security>
        </binding>
      </netTcpBinding>
    </bindings>

クライアント構成

        <bindings>
            <netTcpBinding>
                <binding name="NetTcpBinding_ISVC" closeTimeout="00:01:00" openTimeout="00:10:00"
                    receiveTimeout="23:59:59" sendTimeout="00:01:30" transactionFlow="false"
                    transferMode="Buffered" transactionProtocol="OleTransactions"
                    hostNameComparisonMode="StrongWildcard" listenBacklog="1000"
                    maxBufferPoolSize="671088640" maxBufferSize="671088640" maxConnections="1000"
                    maxReceivedMessageSize="671088640">
                    <readerQuotas maxStringContentLength="671088640" maxArrayLength="671088640"
                        maxBytesPerRead="671088640" />
                    <reliableSession ordered="true" inactivityTimeout="23:59:59"
                        enabled="true" />
                    <security mode="None">
                        <message clientCredentialType="Windows" />
                    </security>
                </binding>
            </netTcpBinding>
        </bindings>

ここに何か問題がありますか?これらの種類のアプリケーションに最適な構成は何ですか?

アップデート:

私は1つのことに遭遇しました:

あるサービス契約では、何かを変更して、接続されているすべてのクライアントに通知します。少なくとも私のテストでは、通常は正常に機能します。しかし、最後の「クラッシュ」または「フリーズ」でログを調べたところ、最新の関数では、コールバックコントラクトを使用してクライアントに通知していることがわかりました。

そこでやりたいこと:データベースに何かを保存し、最後に接続されているすべてのクライアントに変更を通知します。接続されているクライアントのリストはもう最新ではなく、このステップでタイムアウトになると思います。

ここで問題となるのは、これらのタイムアウトを回避する方法です。

  • サービスでスレッドを使用する必要がありますか?サービスコールが終了するとすぐにスレッドが強制終了されると思いますが、私はここにいますか?
  • すべてのコールバック通知を行う静的キュー関数を実装できます(これはMarc_Sが提案したものです)
  • サーバー内の接続ドロップを確実に検出する方法はありますか?
4

2 に答える 2

6

ただの野蛮な考え:クライアントは長期間(数日も)メッセージを送信しているように見えますが、あまり頻繁には送信していないようです-アプリを再設計して、代わりにメッセージキューのようなものを使用することはできますか?コールバック契約?

キューは、2つのシステムを分離し、タイムアウトなどの可能性を減らすための優れた方法です。

キューベースのシナリオでは、クライアントはメッセージをキューにドロップし(たとえば、Windows Serverのすべてのコピーに付属するMSMQ)、サービスはその着信キューでメッセージをリッスンします。サービスはメッセージを取得して処理し、通常、ある種の応答メッセージを2番目のキューに戻します(クライアントはこれをリッスンします)。

ここでの主な利点は次のとおりです。

  • かなり脆弱で複雑なコールバック契約の設定は必要ありません
  • システムは切り離されており、システム間の接続が数秒、数分、数時間切断されても機能し続けます
  • サービスは着信メッセージに応答し、より多くの「ターゲット」応答を送り返すことができます。たとえば、特定のクライアントは「通常の」応答キューでリッスンでき、他のクライアントは「優先」応答キューでリッスンできます。システムは、より柔軟なIMOを提供します。

その他のリソースを参照してください:

のようなものをチェックしてください

および他のシステム(MassTransit et。al。)は、キューに入れられた通信を優先して、システム間で真剣にスケーラブルで信頼性の高いメッセージングを提供します。

于 2010-03-06T14:38:58.270 に答える
0

わかりました、問題は解決しました。

この問題は、ログアウトせずに1人のクライアントが予期せず死亡した場合に発生しました。そのため、接続されているすべてのクライアントにメッセージをブロードキャストすることを希望するサービスからクライアントがメソッドを呼び出すと、タイムアウトが発生し、クライアントがブロックされました。したがって、すべてのコールバックをデリゲート関数にラップすることで、問題を解決することができました。

これが私がそれをした方法です:

public enum CallbackType
{
    callbackfunc1,
    callbackfunc2,
    callbackfunc3
};
public class CallbackEventArgs : EventArgs
{
    public ISVCCallback callback;
    public CallbackType type;
    public string s1;
    public string s2;
    public string s3;
    public List<string> ls1;
}

// Declare delegate
public delegate void SVCEventHandler(object sender, CallbackEventArgs e);

...サービス定義になりました

public string Login(...)
{
    Client cl = new Client(MyCallbackHandler);
    ....

    CallbackEventArgs ce = new CallbackEventArgs();
    ce.callback = cl.CallbackChannel;
    ce.type = CallbackType.callbackfunc1;
    ce.s1 = "Parameter A";
    ce.s2 = "Parameter B";
    cl.MyCallBackHandler.BeginInvoke(this, ce, new AsyncCallback(EndAsync), null);
}

private void MyCallbackHandler(object sender,  CallbackEventArgs e)
    {
        try
        {
            switch (e.type)
            {
                 case CallbackType.callbackfunc1:
                       e.callback.callbackfunc1(e.s1, e.s2);
                       break;
                 default:
                       throw new MissingFieldException(e.type);
            }
    }
    catch(TimeoutException tex){
        // Remove current client, channel timed out
    }
    catch(Exception ex){
        // Do something
    }
}

private void EndAsync(IAsyncResult iar)
    {
        SVCEventHandler d = null;
        try
        {
            System.Runtime.Remoting.Messaging.AsyncResult asres = (System.Runtime.Remoting.Messaging.AsyncResult)iar;
            d = ((SVCEventHandler)asres.AsyncDelegate);
            d.EndInvoke(ar);
        }
        catch(Exception ex)
        {
            // Do something
        }
    }

ありがとう!!

于 2010-03-06T23:46:10.430 に答える