Das Problem besteht in der maximalen Anzahl gleichzeitiger Verbindungen zu einem einzelnen Server in Internet Explorer 7/6. Es sind nur 2! http://msdn.microsoft.com/en-us/library/cc304129(VS.85).aspx
Wenn wir 3 (z. B.) gleichzeitige Dienstanrufe haben, werden zwei von ihnen sofort zum Server gesendet, aber der dritte wartet in der Warteschlange. Auch der Sende-Timer (entspricht sendTimeout
) wird ausgeführt, wenn sich die Anfrage in der Warteschlange befindet. Wenn die ersten beiden Dienstanforderungen für eine lange Zeit ausgeführt werden, generiert der dritte TimeoutException, obwohl es nicht an den Server gesendet wurde (und wir werden keine Informationen über diese Anfrage auf der Server-Seite sehen und kann es nicht mit Fiddler abfangen ...).
In einer realen Situation, wenn wir etwa 12 gleichzeitige Anrufe und Standard 1 Minute senden Timeout und wenn Service Anrufe verarbeiten mehr als 10 Sekunden im Durchschnitt als können wir leicht Timeout Ausnahme mit den letzten zwei Anrufe (12/2 * 10 sec = 60 sec) weil sie alle anderen warten werden.
Die Lösung lautet:
- Minimieren Anzahl gleichzeitiger Service-Anrufe.
- Erhöhen
sendTimeout
Wert in Client Konfig.
- Implementieren Sie die automatische Wiederholungsfunktion für kritische Dienste.
- Implementieren Sie die Warteschlange der Anforderungen, um sie zu verwalten.
In meinem Fall habe ich 1-3 Dinge getan und das war genug.
Hier ist meine Implementierung von Auto-Retry-Funktion:
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
}
}
Und hier ist eine Nutzung:
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 hilfreich sein wird.
Ich habe eine Verwirrung hier .. Wie haben Sie ClientBaseExtender verwendet? Stammt Ihr Proxy von ClientBaseExtender? Aber die Proxy-Klasse ist bereits von System.ServiceModel.ClientBase abgeleitet. Also, wie Plug-in ClientBaseExtender mit MyServiceClient? –
Nein, ClientBaseExtender bietet die Erweiterungsmethode (http://msdn.microsoft.com/en-us/library/bb383977.aspx) für ClientBase. Sie können diese Methode also in allen Erben von ClientBase verwenden. –
Welche ** Protokolldateien ** analysierst du? mit irgendeinem _tool_? – Kiquenet