2009-05-24 105 views
1

分析日志文件我已经注意到,约1%的服务调用以Silverlight客户端的TimeoutException结束。 服务(wcf)非常简单,不需要长时间计算。 根据日志,所有对服务的调用总是在小于1秒内处理(即使在客户端发生TimeoutException时也是如此!),所以它不是服务器超时。从Silverlight应用程序同时调用WCF服务中的TimeoutException

那又怎么了?它可能是配置或网络问题?我怎样才能避免它? 什么额外的日志信息可以帮助本地化这个问题?

我想到的唯一解决方法是在超时后重试服务调用。

我将不胜感激在这个问题上的任何帮助!

更新:在启动时,应用程序会同时执行17个服务调用和12个服务调用(可能是因为失败?)。

更新: WCF日志尚未包含有关此问题的有用信息。看起来有些服务电话没有到达服务器端。

+0

我在这里有一个困惑..你如何使用ClientBaseExtender?您的代理是否从ClientBaseExtender派生?但是代理类已经从System.ServiceModel.ClientBase派生。那么你如何使用MyServiceClient插件ClientBaseExtender? – 2010-08-24 09:40:20

+0

不,ClientBaseExtender为ClientBase提供扩展方法(http://msdn.microsoft.com/en-us/library/bb383977.aspx)。所以你可以在ClientBase的任何继承者中使用这个方法。 – 2010-08-26 13:18:09

回答

6

问题出在Internet Explorer 7/6中对单个服务器的最大并发连接数。它只有2! http://msdn.microsoft.com/en-us/library/cc304129(VS.85).aspx

如果我们有3个(例如)并发服务调用,其中两个将立即发送到服务器,但第三个将在队列中等待。发送计时器(对应于sendTimeout)也在请求处于队列中时运行。如果前两个服务请求会长时间运行,那么第三个服务请求将会生成TimeoutException,尽管它没有发送到服务器(并且我们不会在服务器端看到有关此请求的任何信息,也无法使用Fiddler ...)。

在更真实的情况下,如果我们有大约12个并发呼叫并且默认1分钟发送超时,并且如果服务呼叫平均处理超过10秒,则比我们可以容易地获得最后两个呼叫的超时例外(12/2 * 10秒= 60秒),因为他们会等待所有其他人。

解决的办法是:

  1. 最大限度地减少并发服务呼叫数。
  2. 增加sendTimeout客户端配置。
  3. 实现关键服务的自动重试功能。
  4. 实施请求管理队列。

在我的情况下,我做了1-3件事情,这就够了。

这是我实现自动重试功能:

public static class ClientBaseExtender 
{ 
    /// <summary> 
    /// Tries to execute async service call. If <see cref="TimeoutException"/> occured retries again. 
    /// </summary> 
    /// <typeparam name="TChannel">ServiceClient class.</typeparam> 
    /// <typeparam name="TArgs">Type of service client method return argument.</typeparam> 
    /// <param name="client">ServiceClient instance.</param> 
    /// <param name="tryExecute">Delegate that execute starting of service call.</param> 
    /// <param name="onCompletedSubcribe">Delegate that subcribes an event handler to the OnCompleted event of the service client method.</param> 
    /// <param name="onCompleted">Delegate that executes when service call is succeeded.</param> 
    /// <param name="onError">Delegate that executes when service call fails.</param> 
    /// <param name="maxAttempts">Maximum attempts to execute service call before error if <see cref="TimeoutException"/> occured (by default 5).</param> 
    public static void ExecuteAsyncRepeatedly<TChannel, TArgs>(this ClientBase<TChannel> client, Action tryExecute, 
                   Action<EventHandler<TArgs>> onCompletedSubcribe, EventHandler<TArgs> onCompleted, 
                   EventHandler<TArgs> onError, int maxAttempts) 
     where TChannel : class 
     where TArgs : AsyncCompletedEventArgs 
    { 
     int attempts = 0; 
     var serviceName = client.GetType().Name; 

     onCompletedSubcribe((s, e) => 
           { 
            if (e.Error == null) // Everything is OK 
            { 
             if (onCompleted != null) 
              onCompleted(s, e); 

             ((ICommunicationObject)client).Close(); 
             Debug.WriteLine("[{1}] Service '{0}' closed.", serviceName, DateTime.Now); 
            } 
            else if (e.Error is TimeoutException) 
            { 
             attempts++; 

             if (attempts >= maxAttempts) // Final timeout after n attempts 
             { 
              Debug.WriteLine("[{2}], Final Timeout occured in '{0}' service after {1} attempts.", serviceName, attempts, DateTime.Now); 

              if (onError != null) 
               onError(s, e); 
              client.Abort(); 

              Debug.WriteLine("[{1}] Service '{0}' aborted.", serviceName, DateTime.Now); 
              return; 
             } 

             // Local timeout 
             Debug.WriteLine("[{2}] Timeout occured in '{0}' service (attempt #{1}).", serviceName, attempts, DateTime.Now); 

             Debug.WriteLine("[{2}] Attempt #{0} to execute call to '{1}' service.", attempts + 1, serviceName, DateTime.Now); 
             tryExecute(); // Try again. 
            } 
            else 
            { 
             if (onError != null) 
              onError(s, e); 
             client.Abort(); 
             Debug.WriteLine("[{1}] Service '{0}' aborted.", serviceName, DateTime.Now); 
            } 
           }); 

     Debug.WriteLine("[{2}] Attempt #{0} to execute call to '{1}' service.", attempts + 1, serviceName, DateTime.Now); 
     tryExecute(); // First attempt to execute 
    } 
} 

这里是一个用法:

var client = new MyServiceClient(); 
client.ExecuteAsyncRepeatedly(() => client.MyOperationAsync(...), 
    (EventHandler<MyOperationCompletedEventArgs> handler) => client.MyOperationCompleted += handler, 
    (s, e) => // OnCompleted 
     { 
      Do(e.Result); 
     }, 
    (s, e) => // OnError 
     { 
      HandleError(e.Error); 
     } 
); 

希望这会有所帮助。

0

嗯...有可能请求/响应需要超过64个K或太多的对象序列化?

您可以尝试使用控制台应用程序进行模拟Hiting服务器(只是为了检查它是否为网络SL ...)?

0

你还有这个问题吗?

如果是这样,那么也许你应该用提琴手或微软网络监视器或什么东西看网络?

相关问题