49

SOでこの回答を拡張して、WCFクライアントが一時的なネットワーク障害で再試行し、認証の有効期限などの再試行が必要な他の状況を処理しようとしています。

質問:

処理する必要がある WCF 例外は何ですか?また、それらを処理する正しい方法は何ですか?

以下に、代わりに、または追加して見たいと思っているいくつかのサンプル テクニックを示しますproxy.abort()

  • 再試行する前に X 秒遅らせる
  • New() WCF クライアントを閉じて再作成します。古いものは処分してください。
  • このエラーを再試行して再スローしないでください
  • N 回再試行してからスローする

すべての例外またはそれらを解決する方法を 1 人の人物が知っているとは考えにくいため、知っていることを共有してください。以下のコード サンプルに回答とアプローチをまとめます。

    // USAGE SAMPLE
    //int newOrderId = 0; // need a value for definite assignment
    //Service<IOrderService>.Use(orderService=>
    //{
    //  newOrderId = orderService.PlaceOrder(request);
    //}




    /// <summary>
    /// A safe WCF Proxy suitable when sessionmode=false
    /// </summary>
    /// <param name="codeBlock"></param>
    public static void Use(UseServiceDelegateVoid<T> codeBlock)
    {
        IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
        bool success = false;
        try
        {
            codeBlock((T)proxy);
            proxy.Close();
            success = true;
        }
        catch (CommunicationObjectAbortedException e)
        {
                // Object should be discarded if this is reached.  
                // Debugging discovered the following exception here:
                // "Connection can not be established because it has been aborted" 
            throw e;
        }
        catch (CommunicationObjectFaultedException e)
        {
            throw e;
        }
        catch (MessageSecurityException e)
        {
            throw e;
        }
        catch (ChannelTerminatedException)
        {
            proxy.Abort(); // Possibly retry?
        }
        catch (ServerTooBusyException)
        {
            proxy.Abort(); // Possibly retry?
        }
        catch (EndpointNotFoundException)
        {
            proxy.Abort(); // Possibly retry?
        }
        catch (FaultException)
        {
            proxy.Abort();
        }
        catch (CommunicationException)
        {
            proxy.Abort();
        }
        catch (TimeoutException)
        {
         // Sample error found during debug: 

         // The message could not be transferred within the allotted timeout of 
         //  00:01:00. There was no space available in the reliable channel's 
         //  transfer window. The time allotted to this operation may have been a 
         //  portion of a longer timeout.

            proxy.Abort();
        }
        catch (ObjectDisposedException )
        {
            //todo:  handle this duplex callback exception.  Occurs when client disappears.  
            // Source: https://stackoverflow.com/questions/1427926/detecting-client-death-in-wcf-duplex-contracts/1428238#1428238
        }
        finally
        {
            if (!success)
            {
                proxy.Abort();
            }
        }
    }
4

4 に答える 4

23

編集:クライアントを複数回閉じたり、再度開いたりすると、いくつかの非効率性があるようです。私はここで解決策を模索しており、見つかった場合はこのコードを更新および拡張します。(または、David Khaykinが回答を投稿した場合は、承認済みとしてマークします)

数年間これをいじくり回した後、以下のコードは、WCFの再試行を処理し、例外を処理するための私の好ましい戦略です(ウェイバックマシンからのこのブログ投稿を見た後)。

私はすべての例外を調査し、その例外で何をしたいのかを調べ、共通の特徴に気づきました。共通の基本クラスから継承された「再試行」を必要とするすべての例外。また、クライアントを無効な状態にするすべてのpermFail例外も、共有基本クラスから発生していることに気付きました。

次の例は、クライアントが通過する可能性のあるすべてのWCF例外をトラップし、独自のカスタムチャネルエラーに対して拡張可能です。

WCFクライアントの使用例

クライアント側プロキシを生成したら、それを実装するために必要なのはこれだけです。

Service<IOrderService>.Use(orderService=>
{
  orderService.PlaceOrder(request);
}

ServiceDelegate.cs

このファイルをソリューションに追加します。再試行の回数や処理する例外を変更する場合を除いて、このファイルを変更する必要はありません。

public delegate void UseServiceDelegate<T>(T proxy);

public static class Service<T>
{
    public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>(""); 

    public static void Use(UseServiceDelegate<T> codeBlock)
    {
        IClientChannel proxy = null;
        bool success = false;


       Exception mostRecentEx = null;
       int millsecondsToSleep = 1000;

       for(int i=0; i<5; i++)  // Attempt a maximum of 5 times 
       {
           // Proxy cann't be reused
           proxy = (IClientChannel)_channelFactory.CreateChannel();

           try
           {
               codeBlock((T)proxy);
               proxy.Close();
               success = true; 
               break;
           }
           catch (FaultException customFaultEx)
           {
               mostRecentEx = customFaultEx;
               proxy.Abort();

               //  Custom resolution for this app-level exception
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }

           // The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
           catch (ChannelTerminatedException cte)
           {
              mostRecentEx = cte;
               proxy.Abort();
               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep  * (i + 1)); 
           }

           // The following is thrown when a remote endpoint could not be found or reached.  The endpoint may not be found or 
           // reachable because the remote endpoint is down, the remote endpoint is unreachable, or because the remote network is unreachable.
           catch (EndpointNotFoundException enfe)
           {
              mostRecentEx = enfe;
               proxy.Abort();
               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }

           // The following exception that is thrown when a server is too busy to accept a message.
           catch (ServerTooBusyException stbe)
           {
              mostRecentEx = stbe;
               proxy.Abort();

               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }
           catch (TimeoutException timeoutEx)
           {
               mostRecentEx = timeoutEx;
               proxy.Abort();

               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           } 
           catch (CommunicationException comException)
           {
               mostRecentEx = comException;
               proxy.Abort();

               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }


           catch(Exception e)
           {
                // rethrow any other exception not defined here
                // You may want to define a custom Exception class to pass information such as failure count, and failure type
                proxy.Abort();
                throw e;  
           }
       }
       if (success == false && mostRecentEx != null) 
       { 
           proxy.Abort();
           throw new Exception("WCF call failed after 5 retries.", mostRecentEx );
       }

    }
}
于 2012-02-28T20:48:45.707 に答える
4

次の機能を備えたCodeplexでプロジェクトを開始しました

  • クライアントプロキシの効率的な再利用を可能にします
  • EventHandlerを含むすべてのリソースをクリーンアップします
  • デュプレックスチャネルで動作します
  • 通話ごとのサービスで動作します
  • 構成コンストラクター、またはファクトリによるサポート

http://smartwcfclient.codeplex.com/

これは進行中の作業であり、非常に多くのコメントが寄せられています。改善に関するフィードバックをいただければ幸いです。

インスタンスモードでの使用例:

 var reusableSW = new LC.Utils.WCF.ServiceWrapper<IProcessDataDuplex>(channelFactory);

 reusableSW.Reuse(client =>
                      {
                          client.CheckIn(count.ToString());
                      });


 reusableSW.Dispose();
于 2011-06-01T00:55:46.400 に答える
2

以下のリンクは、WCF 例外の処理に役立つ場合があります。

http://www.codeproject.com/KB/WCF/WCFErrorHandling.aspx

http://msdn.microsoft.com/en-us/library/cc949036.aspx

于 2011-05-31T09:02:33.557 に答える
2

サーバーでのほぼすべてのタイプの障害に対処する WCF クライアントがあります。Catch リストは非常に長いですが、そうである必要はありません。よく見ると、多くの例外が例外クラス (および他のいくつかのクラス) の子定義であることがわかります。

したがって、必要に応じて、物事を大幅に簡素化できます。とはいえ、ここにいくつかの典型的なエラーがあります。

サーバーのタイムアウト
サーバーがビジー状態
です サーバーを利用できません。

于 2011-05-29T19:37:21.517 に答える