分析日志文件我注意到~1%的服务调用以Silverlight客户端的TimeoutException结束。 服务(wcf)非常简单,不执行长计算。 根据日志,对服务的所有调用总是在不到1秒的时间内处理(即使客户端发生了TimeoutException!),因此它不是服务器超时。
那有什么不对?可以是配置还是网络问题?我怎么能避免呢? 哪些其他日志记录信息可以帮助您本地化此问题?
我想到的唯一一种解决方法是在超时后重试服务调用。
我会很感激这个问题的任何帮助!
更新:启动时,应用程序执行17次服务调用,同时执行12次(可能是失败原因?)。
更新:WCF日志未包含有关此问题的有用信息。似乎有些服务调用没有到达服务器端。
答案 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秒)因为他们会等待所有其他人。
解决方案是:
sendTimeout
值。在我的情况下,我做了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或其他什么来观看网络?