TimeoutException при одновременных вызовах служб WCF из приложения Silverlight

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

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

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

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

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

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


person Alexander K.    schedule 24.05.2009    source источник
comment
У меня тут путаница.. Как вы использовали ClientBaseExtender? Ваш прокси происходит от ClientBaseExtender? Но прокси-класс уже является производным от System.ServiceModel.ClientBase. Так как же подключить ClientBaseExtender к MyServiceClient??   -  person    schedule 24.08.2010
comment
Нет, ClientBaseExtender предоставляет метод расширения (msdn.microsoft.com/en-us/ library/bb383977.aspx) для ClientBase. Таким образом, вы можете использовать этот метод в любых наследниках ClientBase.   -  person Alexander K.    schedule 26.08.2010
comment
Какие файлы журналов вы analyze ? с помощью любого инструмента?   -  person Kiquenet    schedule 27.11.2015


Ответы (3)


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

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

person Alexander K.    schedule 03.06.2009

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

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

person John Saunders    schedule 03.06.2009

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

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

person Braulio    schedule 25.05.2009
comment
возможно ли, что запрос/ответ занимает более 64 КБ или слишком много сериализованных объектов Нет, я думаю. Ответы службы достаточно просты и не превышают 64Кб. Можете ли вы попытаться смоделировать попадание на сервер с помощью консольного приложения (просто чтобы проверить, является ли он сетевым, SL...)? Спасибо за совет! В наших условиях это непросто, но я попытаюсь. - person Alexander K.; 25.05.2009