6

以下のテキストは、この質問を拡張して色を追加するための努力です。

不正なクライアントがサービス全体をダウンさせないようにするにはどうすればよいですか?

私は基本的にこのシナリオを持っています: WCF サービスは、クライアント コールバックで稼働しており、単純な一方向通信であり、これとそれほど違いはありません。

public interface IMyClientContract
{
  [OperationContract(IsOneWay = true)]
  void SomethingChanged(simpleObject myObj);
}

このメソッドを、サービスから最終的には約 50 の同時接続クライアントに対して、1 秒間に何千回も呼び出す可能性があります。レイテンシは可能な限り低くします (15 ミリ秒未満が望ましい)。これは、サーバーに接続されているクライアント アプリの1 つにブレーク ポイントを設定するまで正常に動作し、サービスがハングしてから 2 ~ 5 秒後にすべてがハングし、サービスが終了するまで約 30 秒間、他のクライアントはデータを受信しません。接続障害イベントを登録し、問題のあるクライアントを切断します。この後、他のすべてのクライアントは引き続きメッセージを受信します。

serviceThrottling、同時実行性の調整、スレッドプールの最小スレッドの設定、WCF の秘密のソース、および 9 ヤード全体について調査しましたが、1 日の終わりに、この記事MSDN - WCF の要点、一方向の呼び出し、コールバック、およびイベント について正確に説明しています。実際に推奨せずに私が抱えている問題。

サービスが安全にクライアントにコールバックできるようにする 3 つ目の解決策は、コールバック コントラクト操作を一方向操作として構成することです。そうすることで、同時実行性がシングルスレッドに設定されている場合でも、サービスがコールバックできるようになります。これは、ロックを争う応答メッセージがないためです。

しかし、記事の前半で、クライアントの観点からのみ、私が見ている問題について説明しています

一方向の呼び出しがサービスに到達すると、すべてが一度にディスパッチされず、サービスで構成された同時実行モードの動作とセッション モードに従って、一度に 1 つずつディスパッチされるようにサービス側でキューに入れられる場合があります。サービスがキューに入れるメッセージ (一方向または要求/応答) の数は、構成されたチャネルと信頼性モードの積です。キューに入れられたメッセージの数がキューの容量を超えた場合、一方向の呼び出しを発行しても、クライアントはブロックされます。

クライアントへのキューに入れられたメッセージの数がキューの容量を超えており、スレッドプールがこのクライアントを呼び出そうとするスレッドでいっぱいになり、すべてブロックされているとしか考えられません。

これを処理する正しい方法は何ですか? クライアントごとにサービス通信レイヤーでキューに入れられているメッセージの数を確認し、特定の制限に達した後に接続を中止する方法を調査する必要がありますか?

満杯のキューで WCF サービス自体がブロックされている場合、サービス内に実装できるすべての非同期/一方向/ファイア アンド フォーゲット戦略は、1 つのクライアントのキューがいっぱいになるたびにブロックされます。

4

2 に答える 2

1

アップデート:

クライアントのコールバック チャネルを呼び出すためのFire-and-forgetセットアップを実装しました。クライアントのバッファがいっぱいになると、サーバーはブロックしなくなりました。

MyEvent は、WCF クライアント コントラクトで定義されたメソッドの 1 つに一致するデリゲートを持つイベントです。それらが接続すると、本質的にコールバックをイベントに追加します。

MyEvent += OperationContext.Current.GetCallbackChannel<IFancyClientContract>().SomethingChanged

など...そして、このデータをすべてのクライアントに送信するために、次のことを行っています

//serialize using protobuff
using (var ms = new MemoryStream())
{
    ProtoBuf.Serializer.Serialize(ms, new SpecialDataTransferObject(inputData));
    byte[] data = ms.GetBuffer();
    Parallel.ForEach(MyEvent.GetInvocationList(), p => ThreadUtil.FireAndForget(p, data));
}

ThreadUtil クラスで、火と霧の記事で定義されているコードに本質的に次の変更を加えました

static void InvokeWrappedDelegate(Delegate d, object[] args)
{
    try
    {
        d.DynamicInvoke(args);
    }
    catch (Exception ex)
    {
        //THIS will eventually throw once the client's WCF callback channel has filled up and timed out, and it will throw once for every single time you ever tried sending them a payload, so do some smarter logging here!!
        Console.WriteLine("Error calling client, attempting to disconnect.");
        try
        {
            MyService.SingletonServiceController.TerminateClientChannelByHashcode(d.Target.GetHashCode());//this is an IContextChannel object, kept in a dictionary of active connections, cross referenced by hashcode just for this exact occasion
        }
        catch (Exception ex2)
        {
            Console.WriteLine("Attempt to disconnect client failed: " + ex2.ToString());
        }
    }
}

サーバーがまだ配信されるかどうかを確認するために待機しているすべての保留中のパケットを削除する方法がわかりません。最初の例外を取得したら、理論的にはどこかのキューにある他のすべてのリクエストを終了できるはずですが、このセットアップは機能しており、目的を満たしています。

于 2011-07-14T16:17:24.437 に答える
1

クライアントのコールバックについてはよくわかりませんが、一般的な wcf コードのブロックの問題に似ているように思えます。多くの場合、BackgroundWorker を生成し、スレッドでクライアント呼び出しを実行することで、これらの問題を解決します。その間、メイン スレッドは子スレッドの実行時間をカウントします。子プロセスが数ミリ秒以内に終了しない場合、メイン スレッドは処理を続行してスレッドを放棄します (最終的にはスレッド自体が終了するため、メモリ リークは発生しません)。これは基本的に、Graves 氏が「fire-and-forget」というフレーズで提案していることです。

于 2011-07-14T15:49:23.603 に答える