web-dev-qa-db-ja.com

障害状態に入る永続的なWCFクライアントを処理します

Webアプリから使用しているWCFサービスがあります。使用しているクライアントは、VisualStudioの[サービス参照の追加]オプションを使用して生成されました。これはWebアプリであり、アプリの性質上、セッションが比較的短くなる可能性があるため、ユーザーがログインしたときにクライアントのインスタンスを作成し、セッションの間保持することを選択しました。セッションが終了したら、それを破棄します。

これは私の質問に私をもたらします-私たちは障害状態に入るクライアントのチャネルを処理するための最良の方法を決定しようとしています。いくつかを検索した後、私たちはこれを思いついた:

if(client.State = CommuncationState.Faulted)
{
    client = new Client();
}

try
{
    client.SomeMethod();
}
catch //specific exceptions left out for brevity
{
    //logging or whatever we decide to do
    throw;
}

ただし、少なくともこの場合、サービスがダウンしていても、実際にそれを使用して電話をかけようとするまで、クライアントはOpen状態を表示するため、これは機能しません。次に、それがFaulted状態に入るポイント。

だから、これは私たちに何か他のことをすることを任せます。私たちが思いついた別のオプションは次のとおりです。

try
{
    client.SomeMethod();
}
catch
{
    if(client.State == CommunicationState.Faulted)
    {
        //we know we're faulted, try it again
        client = new Client();
        try
        {
            client.SomeMethod();
        }
        catch
        {
            throw;
        }
    }
    //handle other exceptions
}

しかし、それはにおいがします。明らかに、新しいクライアントを使用し、呼び出しごとにそれを破棄することで、これを回避できます。それは不必要に思えますが、それが正しい方法であれば、それが私たちが選ぶものだと思います。では、クライアントが障害状態にあるかどうかを判断し、それに対して何かを実行するための最善の方法は何でしょうか。本当にすべての呼び出しに対して新しいクライアントを取得する必要がありますか?

もう1つ覚えておくべきことは、クライアントのインスタンス化とこのすべてのチェックと処理は、クライアントのラッパークラスで行われます。これを意図した方法で行うと、アプリ自体に対して透過的になります。呼び出しを行ったり、それらから例外を処理したりするために、特別なコードは必要ありません。

19
Zann Anderson

質問に答えるには、ChannelFactoryプロパティの Faulted event を次のように処理できます。

client.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted);

これにより、必要なロギング/クリーンアップを実行できるようになります。

一般的な推奨事項として、セッション中はチャネルを開いたままにしないでください。終了したら、チャネルを適切に閉じていることを確認してください(例外的に中止します)。

また、可能であれば、Visual Studio Add Service Referenceを使用しないこと、または少なくとも生成されたコード/構成をクリーンアップすることを検討してください。プロキシ実装を使用する場合は、 ClientBase から派生して独自の実装を作成するか、 ChannelFactory 実装を使用することをお勧めします。ラッパークラスについて言及しているので、ChannelFactoryを使用して、クリーンアップのニーズに合わせて Faulted event を処理することをお勧めします。

20
BrandonZeider

クライアントプロキシで.Faultedイベントを処理してみてください。例:

_((ICommunicationObject)client).Faulted += new EventHandler(client_Faulted);

private void client_Faulted(object sender, EventArgs e)
{
    client = new Client();
    ((ICommunicationObject)client).Faulted += new EventHandler(client_Faulted);
}
_

チャネルに障害が発生した瞬間にトリガーされ、チャネルを再度開く機会が与えられます。

また、clientメソッドへの各呼び出しをtry-catchブロックでラップする必要があります。また、呼び出しをn回再試行して失敗をログに記録するwhile()ループでラップすることもできます。例えば:

_bool succeeded = false;
int triesLeft = 3;
while (!succeeded && triesLeft > 0)
{
    triesLeft--;
    try
    {
        client.SomeMethod();
        succeeded = true;
    }
    catch (exception ex)
    {
        logger.Warn("Client call failed, retries left: " + triesLeft;
    }
}
if (!succeeded)
    logger.Error("Could not call web service");
_

私のコードでは、ManualResetEventを使用してwhile()ループをブロックし、_client_Faulted_イベントハンドラーがclientを再作成する機会を得ました。プロキシ。

13
Chris Wenham