TimeoutException при одновременных вызовах служб WCF из приложения Silverlight - PullRequest
1 голос
/ 24 мая 2009

Анализируя файлы журналов Я заметил, что ~ 1% вызовов служб заканчивался с исключением TimeoutException на стороне клиента Silverlight. Сервисы (wcf) довольно просты и не выполняют длинных вычислений. Согласно журналу, все обращения к сервисам всегда обрабатываются менее чем за 1 секунду (даже если на клиенте возникла исключительная ситуация TimeoutException!), Поэтому это не тайм-аут сервера.

Так что не так? Это может быть проблема конфигурации или сети? Как я могу избежать этого? Какие дополнительные данные журналов могут быть полезны для локализации этой проблемы?

Единственный обходной путь, который я придумал, - это повторить вызовы службы после истечения времени ожидания.

Буду признателен за любую помощь в этом вопросе!

Обновление: При запуске приложение выполняет 17 сервисных вызовов и 12 из них одновременно (может ли это быть причиной сбоя?).

Обновление: Журнал WCF не содержит полезной информации об этой проблеме. Кажется, что некоторые сервисные вызовы не достигают серверной стороны.

Ответы [ 3 ]

6 голосов
/ 04 июня 2009

Проблема заключается в максимальном количестве одновременных подключений к одному серверу в 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 значение в клиенте config.
  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);
        }
);

Надеюсь, это будет полезно.

0 голосов
/ 04 июня 2009

У вас все еще есть эта проблема?

Если это так, то, возможно, вам следует смотреть сеть с помощью Fiddler или Microsoft Network Monitor или чего-то еще?

0 голосов
/ 25 мая 2009

Мммм ... возможно ли, что запрос / ответ занимает более 64 К или слишком много объектов сериализовано?

Можете ли вы попытаться создать симуляцию поражения сервера консольным приложением (просто чтобы проверить, является ли он сетевым, SL ...)?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...