5

サーバーの CPU 負荷が高い場合、またはスレッド プールがビジー状態の場合の WCF Reliable Sessions の障害

サーバーの CPU 負荷が高い (80 ~ 100% の範囲) 場合、または使用可能な即時 IO スレッドプール スレッドがない場合に、インフラストラクチャ キープアライブ メッセージの発行または受け入れを妨げる WCF Reliable Sessions に設計上の欠陥があるようです。メッセージを処理します。この症状は、信頼できるセッションの非アクティブ タイムアウトが原因で明らかにランダムなチャネルが異常終了することとして現れます。ただし、キープアライブ タイマーが実行できないにもかかわらず、アボート タイマーが起動しているように見えるため、アボート ロジックはより高い優先度で、または別のメカニズムを介して実行されているようです。

参照元を掘り下げると、ChannelReliableSession は InterruptableTimer クラスを使用して inactivityTimer を処理しているようです。応答として、ReliableOutputSessionChannel によって設定された PollingCallback を起動し、ACKRequestedMessage を作成してリモート エンドポイントに送信します。InactivityTimer は、WCF 内部の IOThreadTimer/IOThreadScheduler を使用して、それ自体をスケジュールします。これは、タイマーを処理するために使用可能な (ビジーでない) IO ThreadPool スレッドに依存します。CPU 負荷が高い場合、スレッド プールは新しいスレッドを生成しないようです。その結果、複数のスレッドが実行されている場合 (私の 4 コア マシンでは 8 スレッドのように見えます。15 秒の inactivityTimeout で 7 は中止されて失敗します)、キープアライブを送信するスレッドは使用できません。ただし、クライアントの信頼できるセッションの非アクティブ タイムアウトをサーバーよりも長くなるように変更すると、これらの条件下でもサーバーはメッセージをより短い時間で期待しているため、一方的にチャネルを中止します。そのため、中止ロジックがより高い優先度で実行されているか、実行中のスレッドの 1 つに例外がスローされているようです (どれかは不明)。CPU の使用率が高いためにサーバーでの中止が遅れ、クライアントの長いタイムアウトが最終的にヒットすることを期待していましたが、そうではありませんでした。CPU 負荷が低い場合、これとまったく同じシナリオが、返されるまでに 30 ~ 90 秒かかる同時呼び出しでも完全に正常に機能します。

InstanceMode が何であるか、最大同時接続、セッション、またはインスタンスが何であるか、他のタイムアウト値が何であるかは関係ありません (recieveTimeout 以外は inactivityTimeout より大きくなければなりません)。これは完全に WCF 実装の設計上の欠陥です。偽のアボートが生成されないように、分離された優先度の高いスレッドまたはリアルタイム スレッドを使用してキープアライブ メッセージを処理する必要があります。

短いバージョンは次のとおりです。CPU負荷が低いままである限り、15秒の信頼できるセッション非アクティブタイムアウトで完了するのに60秒かかる1000の同時リクエストを問題なく発行できます。CPU負荷が高くなるとすぐに、呼び出しはランダムにこれには、CPU 時間をまったく消費していない呼び出しや、使用されるのを待っているアイドリング状態のデュプレックス セッションが含まれます。受信呼び出しも CPU 負荷に追加されると、他の要求が受信キューに留まっている間、中断が保証された要求で実行時間が浪費されるため、サービスは死のスパイラルに入ります。サービスは、すべての要求が停止され、進行中のすべてのスレッドが終了し、CPU 負荷が低下するまで、正常な状態に戻ることはできません。この動作は、逆説的に Reliable Sessions を最も信頼性の低い通信メカニズムの 1 つにしているように見えます。

これと同じ動作がクライアントにも当てはまります。その場合、WCF クライアントはボックス上の他のプロセスに翻弄される可能性がありますが、CPU 負荷が高い場合、すべての操作が inactivityTimeout 未満で完了しない限り、信頼できるセッションをランダムに中止します。ただし、新しい呼び出しを発行しない場合すぐに、WCF は引き続きキープアライブの送信に失敗し、チャネルに障害が発生する可能性があります。

4

1 に答える 1

5

私の答えを文書化する:

ThreadPool.SetMinThreads(X, Y) を使用すると、問題をわずかに軽減できます。ここで、Y は、同時 WCF 要求を実行するスレッドの数よりも大きい数値です。次に、キープアライブにサービスを提供するために利用可能な++スレッドが存在する可能性があり、信頼できるセッションは、持続的な100%のCPU負荷の下でもタイムアウトしない可能性がありますが、これにも限界があります. 私のテストでは、IO スレッドを最小値の 2 から 20 に上げてから、多数の同時実行を発行しました (ただし、何もしない要求は単に 10 秒間スリープするだけです)。その後、クライアントを再実行しましたが、CPU を浪費する呼び出しを使用して、8 つすべてを同時に正常に実行できました。サービスを再起動してすぐに同じクライアント テストを実行すると、スレッド プールの遅延初期化が原因で失敗しました。これを増やしていくと、最終的に 14 回の同時呼び出し (10 回の呼び出しが中止された) で再びタイムアウトになり始めました。IO スレッドを取得して優先順位を上げることができれば、この問題を解決できるのではないかと思います。

++プールは遅延初期化を使用するため、完了するまでに時間がかかるが CPU を使用しない (例: Thread.Sleep(5000)) クライアントから十分な数の同時呼び出しを発行して、プールに強制的にhigh-CPU-blocks-new-threads ロジックをトリガーせずに最小数のスレッドを作成しないと、最小数のスレッドが作成されず、問題が引き続き発生します。

もう 1 つの潜在的な修正方法は、inactivityTimeout を非常に大きな値にすることです。これは問題を軽減するのに役立ちますが、クライアントが意図せずに接続を閉じることができなかった場合でも、サービス拒否の脆弱性が新たに発生します。

それ以外の場合、現時点ではこの問題の修正はないようです。個人的には、この欠陥のために Reliable Sessions を使用しないことをお勧めします。これは、中止された接続と中止が発生し始める状況の両方で中止がランダムになるためです。

于 2013-04-05T17:51:05.950 に答える