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

发布于 2024-07-21 22:29:29 字数 453 浏览 4 评论 0原文

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

那么有什么问题吗? 会是配置问题还是网络问题? 我怎样才能避免它? 哪些附加日志信息有助于定位此问题?

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

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

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

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

Analysing log files I've noticed that ~1% of service calls ended with TimeoutException on the Silverlight client side.
The services (wcf) are quite simple and do not perform long computations.
According the log all calls to the services are always processed in less that 1 sec (even when TimeoutException is occurred on the client!), so it is not server timeout.

So what is wrong? Can it be configuration or network problem? How can I avoid it?
What additional logging information can be helpful for localizing this issue?

The only one workaround I've thought up is to retry service calls after timeout.

I will appreciate any help on this issue!

Update: On startup the application performs 17 service calls and 12 of them simultaneously (may it be cause of failure?).

Update: WCF log has not contained useful information about this issue. It seems some service calls do not reach the server side.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

琉璃梦幻 2024-07-28 22:29:29

问题在于 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);
        }
);

希望这会有所帮助。

The problem is in maximum number of concurrent connections to a single server in Internet Explorer 7/6. It is only 2! http://msdn.microsoft.com/en-us/library/cc304129(VS.85).aspx

If we have 3 (for ex.) concurrent service calls two of them will be sent to server immediately but the third will be waiting in queue. Also the send timer (corresponded to sendTimeout) is running when request is in queue. If the first two service requests will be running for a long time then the third will generate TimeoutException though it wasn't sent to the server (and we won't see any information about this request on the server side and cannot catch it with Fiddler...).

In more real situation if we have about 12 concurrent calls and default 1 min send timeout and if service calls process more than 10 seconds in average than we can easily get timeout exception with the last two calls (12 / 2 * 10 sec = 60 sec) because they will wait all others.

The solution is:

  1. Minimize number of concurrent service calls.
  2. Increase sendTimeout value in client config.
  3. Implement auto retry feature for critical services.
  4. Implement queue of requests to manage them.

In my case I've done 1-3 things and that was enough.

Here is my implementation of auto retry feature:

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

And here is a usage:

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

Hope this will be helpful.

娜些时光,永不杰束 2024-07-28 22:29:29

您还有这个问题吗?

如果是这样,那么也许您应该使用 Fiddler 或 Microsoft Network Monitor 等来监视网络?

Are you still having this problem?

If so, then perhaps you should watch the network with Fiddler or Microsoft Network Monitor or something?

染火枫林 2024-07-28 22:29:29

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

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

Mmmm... can it be possible that the request / response takes more than 64 K's or too much objects serialized?

Can you try to make a simulation hiting the server with a console application (just to check if it's network, SL...)?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文