代码之家  ›  专栏  ›  技术社区  ›  Alexander K.

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

  •  1
  • Alexander K.  · 技术社区  · 15 年前

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

    那怎么了?是配置问题还是网络问题?我怎样才能避免呢? 哪些其他日志信息有助于本地化此问题?

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

    在这个问题上,我将非常感谢您的帮助!

    更新: 启动时,应用程序同时执行17个服务调用和12个服务调用(可能是故障原因吗?).

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

    3 回复  |  直到 9 年前
        1
  •  6
  •   Alexander K.    14 年前

    问题在于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. 增加 发送超时 价值在 客户机 配置。
    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);
            }
    );
    

    希望这会有所帮助。

        2
  •  0
  •   John Saunders    15 年前

    你还有这个问题吗?

    如果是这样的话,也许你应该用小提琴或微软网络监视器之类的东西看网络?

        3
  •  0
  •   Kiquenet user385990    9 年前

    嗯…请求/响应可能需要超过64K的时间,或者序列化的对象太多?

    你能试着用一个控制台应用程序对服务器进行模拟攻击吗(只是为了检查它是不是网络,SL…?

    推荐文章