Клиент WCF приводит к зависанию сервера до сбоя подключения - PullRequest
6 голосов
/ 11 июля 2011

Приведенный ниже текст является попыткой расширить и добавить цвет к этому вопросу:

Как мне не допустить, чтобы ненадлежащий клиент отказался от обслуживания всего сервиса?

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

public interface IMyClientContract
{
  [OperationContract(IsOneWay = true)]
  void SomethingChanged(simpleObject myObj);
}

Я вызываю этот метод потенциально тысячи раз в секунду изобслуживание до 50 одновременно работающих клиентов с минимально возможной задержкой (было бы неплохо <15 мс).Это работает нормально, пока я не установлю точку останова на <em>одном клиентских приложений, подключенных к серверу, а затем все зависает, возможно, через 2-5 секунд служба зависает, и ни один из других клиентов не получает никаких данных в течение примерно 30секунд или около того, пока служба не зарегистрирует событие сбоя соединения и не отключит клиент, который нарушил работу.После этого все остальные клиенты продолжают веселиться, получая сообщения.

Я провел исследование по обслуживанию Throttling, настройке параллелизма, настройке минимальных потоков потоков, секретных соусов WCF и целых 9 ярдов, но в концедень, когда эта статья MSDN - основы WCF, односторонние вызовы, обратные вызовы и события точно описывает проблему, с которой я сталкиваюсь, но не вынесла рекомендации.

Третье решение, котороепозволяет службе безопасно перезванивать клиенту, если контрактные операции обратного вызова настроены как односторонние операции.Это позволяет службе перезванивать, даже если для параллелизма задан однопоточный режим, потому что не будет никакого ответного сообщения, которое будет бороться за блокировку.

, но ранее в этой статье описана проблемаЯ вижу, только с точки зрения клиента

Когда однонаправленные вызовы достигают службы, они могут отправляться не сразу и могут быть поставлены в очередь на стороне службы для отправки по одномувремя, все в соответствии с настроенным сервисом поведением режима параллелизма и режимом сеанса.Сколько сообщений (односторонних или запросов-ответов) служба готова поставить в очередь, является продуктом настроенного канала и режима надежности.Если количество сообщений в очереди превысило емкость очереди, то клиент заблокирует даже при отправке одностороннего вызова

Я могу только предположить, что обратное значение истинно, количество сообщений в очередичтобы клиент превысил емкость очереди, и пул потоков теперь заполнен потоками, пытающимися вызвать этого клиента, которые теперь все заблокированы.

Каков правильный способ справиться с этим?Должен ли я найти способ проверить, сколько сообщений находится в очереди на служебном коммуникационном уровне для каждого клиента, и прервать их соединения после достижения определенного предела?

Почти кажется, что если сама служба WCF блокирует заполнение очереди, то все стратегии async / oneway / fire-and-Forgot, которые я когда-либо мог реализовать в службе, будут блокироваться всякий раз, когда очередь одного клиентанаполняется.

Ответы [ 2 ]

1 голос
/ 14 июля 2011

Обновление:

Я реализовал настройку Fire-and-Forgot для вызова канала обратного вызова клиента, и сервер больше не блокируется после заполнения буфера доclient

MyEvent - это событие с делегатом, которое соответствует одному из методов, определенных в клиентском контракте WCF. Когда они подключаются, я по существу добавляю обратный вызов к событию

MyEvent += OperationContext.Current.GetCallbackChannel<IFancyClientContract>().SomethingChanged

и т. д.... и затем, чтобы отправить эти данные всем клиентам, я делаю следующее

//serialize using protobuff
using (var ms = new MemoryStream())
{
    ProtoBuf.Serializer.Serialize(ms, new SpecialDataTransferObject(inputData));
    byte[] data = ms.GetBuffer();
    Parallel.ForEach(MyEvent.GetInvocationList(), p => ThreadUtil.FireAndForget(p, data));
}

в классе ThreadUtil. По сути, я внес следующие изменения в код, определенный в статье fire-and-foget

static void InvokeWrappedDelegate(Delegate d, object[] args)
{
    try
    {
        d.DynamicInvoke(args);
    }
    catch (Exception ex)
    {
        //THIS will eventually throw once the client's WCF callback channel has filled up and timed out, and it will throw once for every single time you ever tried sending them a payload, so do some smarter logging here!!
        Console.WriteLine("Error calling client, attempting to disconnect.");
        try
        {
            MyService.SingletonServiceController.TerminateClientChannelByHashcode(d.Target.GetHashCode());//this is an IContextChannel object, kept in a dictionary of active connections, cross referenced by hashcode just for this exact occasion
        }
        catch (Exception ex2)
        {
            Console.WriteLine("Attempt to disconnect client failed: " + ex2.ToString());
        }
    }
}

У меня нет хороших идей, как пойти и убить все ожидающие пакеты, которые сервер все еще ждет, чтобы увидеть, будут ли они доставлены.Как только я получу первое исключение, я теоретически должен быть в состоянии пойти и завершить все другие запросы в какой-то очереди, но эта настройка работает и отвечает целям.

1 голос
/ 14 июля 2011

Не знаю много о клиентских обратных вызовах, но это похоже на общие проблемы блокировки кода wcf. Я часто решаю эти проблемы, вызывая BackgroundWorker и выполняя клиентский вызов в потоке. В течение этого времени основной поток считает, сколько времени занимает дочерний поток. Если дочерний процесс не завершил работу в течение нескольких миллисекунд, основной поток просто перемещается и покидает поток (в конечном итоге он сам умирает, поэтому утечка памяти отсутствует). Это в основном то, что мистер Грейвс предлагает с помощью фразы «запусти и забудь».

...