从Silverlight应用程序同时调用WCF服务时出现TimeoutException

时间:2009-05-24 11:50:05

标签: wcf silverlight timeout timeoutexception simultaneous-calls

分析日志文件我注意到~1%的服务调用以Silverlight客户端的TimeoutException结束。 服务(wcf)非常简单,不执行长计算。 根据日志,对服务的所有调用总是在不到1秒的时间内处理(即使客户端发生了TimeoutException!),因此它不是服务器超时。

那有什么不对?可以是配置还是网络问题?我怎么能避免呢? 哪些其他日志记录信息可以帮助您本地化此问题?

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

我会很感激这个问题的任何帮助!

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

更新:WCF日志未包含有关此问题的有用信息。似乎有些服务调用没有到达服务器端。

3 个答案:

答案 0 :(得分: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. 实施管理它们的请求队列。
  5. 在我的情况下,我做了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);
            }
    );
    

    希望这会有所帮助。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

你还有这个问题吗?

如果是这样,那么也许您应该使用Fiddler或Microsoft Network Monitor或其他什么来观看网络?