我正在尝试扩展this answer on SO,以使WCF客户端在出现瞬时网络故障时重试并处理other situations that require a retry such as authentication expiration.

问题:

需要处理哪些WCF异常,正确的处理方式是什么?

除了proxy.abort()之外,还有一些我希望看到的示例技术:

  • 重试
  • 之前延迟X秒
  • 关闭并重新创建New()WCF客户端。处置旧的。
  • 不要重试并重新抛出此错误
  • 重试N次,然后抛出

  • 由于不太可能一个人知道所有异常或解决异常的方法,因此请分享您所知道的。我将在下面的代码示例中汇总答案和方法。
        // 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();
                }
            }
        }
    

    最佳答案

    编辑:多次关闭和重新打开客户端似乎有些效率低下。我是exploring solutions here,如果找到一个,它将更新并扩展此代码。 (或者,如果David Khaykin发布了答案,我会将其标记为已接受)

    经过几年的修改后,下面的代码是我首选的处理WCF重试和处理异常的策略(after seeing this blog posting from the wayback machine)。

    我调查了每个异常,以及该异常的处理方法,并注意到了一个共同特征。每个需要从通用基类继承的“重试”的异常。我还注意到,使客户端进入无效状态的每个permFail异常也来自共享基类。

    下面的示例捕获客户端可以通过的每个WCF异常,并且可以针对您自己的自定义 channel 错误进行扩展。

    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 );
           }
    
        }
    }
    

    08-26 16:51
    查看更多