WCF, BasicHttpBinding: Остановите новые соединения, но позвольте существующим соединениям продолжаться

.NET 3.5, VS2008, сервисное использование WCF BasicHttpBinding

Мне разместили сервис WCF в службе Windows. Когда служба Windows закрывается, из-за обновлений, планового обслуживания, и т.д., я должен корректно закрыть свой сервис WCF. Сервис WCF имеет методы, которые могут взять до нескольких секунд для завершения, и типичный объем является 2-5 вызовами метода в секунду. Я должен закрыть сервис WCF способом, который позволяет, любой ранее называет методы для завершения при отклонении любых новых вызовов. Этим способом я могу достигнуть тихого состояния в ~ 5-10 секунд и затем завершить цикл завершения работы моей службы Windows.

Вызов ServiceHost. Близко походит на правильный подход, но он закрывает соединения клиента сразу же, не ожидая никаких происходящих методов для завершения. Мой сервис WCF завершает свой метод, но нет никого для отправки ответа на, потому что клиент был уже разъединен. Это - решение, предложенное этим вопросом.

Вот последовательность событий:

  1. Клиент называет метод на сервисе, использование VS генерировало прокси-класс
  2. Сервис начинает осуществление сервисного метода
  3. Сервис получает запрос для закрытий
  4. Служебные вызовы ServiceHost. Близко (или BeginClose)
  5. Клиент разъединяется и получает Систему. ServiceModel. CommunicationException
  6. Сервис завершает сервисный метод.
  7. В конечном счете сервис обнаруживает его, больше не имеет работы, чтобы сделать (через прикладную логику) и завершается.

То, в чем я нуждаюсь, для соединений клиента, которые будут сохранены открытым, таким образом, клиенты знают что их сервисные методы, завершенные успешно. Прямо сейчас они просто получают закрытое соединение и не знают если сервисный метод, завершенный успешно или нет. До использования WCF я использовал сокеты и смог сделать это путем управления Сокетом непосредственно. (т.е. остановите Принять цикл, в то время как тихое выполнение Получает и Отправляет),

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

Существует ли способ выполнить это в WCF?

Вещи я попробовал:

  1. ServiceHost. Близко () - закрывает клиенты сразу же
  2. ServiceHost. ChannelDispatchers - звонят Слушателю. Близко () на каждом - кажется, ничего не делает
  3. ServiceHost. ChannelDispatchers - звонят, CloseInput () на каждом - закрывает клиенты сразу же
  4. Переопределение ServiceHost. OnClosing () - позволяет мне задержать Завершение, пока я не решаю, что нормально закрываться, но новые соединения позволяются в это время
  5. Удалите конечную точку с помощью техники, описанной здесь. Это вытирает все.
  6. Выполнение сетевого анализатора для наблюдения ServiceHost. Близко (). Хост просто закрывает соединение, никакой ответ не отправляется.

Спасибо

Править: К сожалению, я не могу реализовать консультативный ответ прикладного уровня, который закрывает система, потому что клиенты в поле уже развертываются. (Я только управляю сервисом, не клиентами),

Править: Я использовал Отражатель Redgate для рассмотрения реализации Microsoft ServiceHost. Близко. К сожалению, это называет некоторых internal классы помощника, к которым не может получить доступ мой код.

Править: Я не нашел полное решение, которое я искал, но предложение Benjamin для использования IMessageDispatchInspector для отклонения запросов до ввода сервисного метода прибыло самое близкое.

11
задан Community 23 May 2017 в 12:25
поделиться

8 ответов

Предположение:

Вы пытались захватить привязку во время выполнения (из конечных точек), преобразовать ее в BasicHttpBinding и (пере) определить свойства там?

Лучшие предположения от меня :

  • OpenTimeout
  • MaxReceivedMessageSize
  • ReaderQuotas

Они могут быть установлены во время выполнения в соответствии с документацией и, кажется, допускают желаемое поведение (блокирование новых клиентов). Это не помогло бы с частью "восходящий брандмауэр / балансировщик нагрузки должен перенаправить".

Последнее предположение: можете ли вы (в документе сказано да, но я не уверен, каковы будут последствия) переопределить адрес конечные точки на адрес localhost по запросу? Это может работать как «закрытие порта» для хоста брандмауэра, если оно все равно не убивает всех клиентов ..

Редактировать: Играя с предложениями выше и ограниченным тестом, я начал играть с инспектором сообщений Комбинация / behavior, которая пока выглядит многообещающей:

public class WCFFilter : IServiceBehavior, IDispatchMessageInspector {
    private readonly object blockLock = new object();
    private bool blockCalls = false;

    public bool BlockRequests {
        get {
            lock (blockLock) {
                return blockCalls;
            }
        }
        set {
            lock (blockLock) {
                blockCalls = !blockCalls;
            }   
        }

    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {          
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) {         
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {
        foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers) {
            foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) {
                endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
            }
        } 
    }

    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) {
        lock (blockLock) {
            if (blockCalls)
                request.Close();
        }
        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState) {           
    }
}

Забудьте об использовании дрянных блокировок и т. д., но используйте это с очень простым тестом WCF (возвращающим случайное число с Thread.Sleep внутри), например:

var sh = new ServiceHost(new WCFTestService(), baseAdresses);

var filter = new WCFFilter();
sh.Description.Behaviors.Add(filter);

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

// Я создаю 3 потока Запрос номера…
Запрос номера…
Запрос номера ..
// Журнал на стороне сервера для одного входящего запроса
Входящий запрос на номер.
// Основной цикл переворачивает "все блокировать" bool
Блокировка доступа с этого момента.
// Для удобства после этого еще 3 клиента
Запрос номера…
Запрос номера…
Запрос номера…
// Первый запрос (с журналом на стороне сервера, см. Выше) завершается успешно
Поступило 1569129641
// Все остальные сообщения еще не дошли до сервера и умирают с ошибкой
Ошибка в запросе клиента, возникшем после блока.
Ошибка в клиентском запросе, порожденном после блока.
Ошибка в клиентском запросе, возникшем после блока.
Ошибка клиентского запроса перед блокировкой.
Ошибка в запросе клиента перед блоком.

6
ответ дан 3 December 2019 в 10:26
поделиться

Есть ли API для восходящего межсетевого экрана? В нашем приложении мы делаем это так, чтобы на уровне балансировщика нагрузки не поступали новые запросы, а затем, когда все запросы завершили обработку, мы можем перезапустить серверы и службы.

2
ответ дан 3 December 2019 в 10:26
поделиться

Я предлагаю установить EventHandler, когда ваша служба переходит в «состояние остановки», используйте метод OnStop. Установите EventHandler, указывающий, что ваша служба переходит в состояние остановки.

Ваш обычный цикл обслуживания должен проверять, установлено ли это событие, если это так, вернуть вызывающему клиенту сообщение «Служба останавливает» и не позволять ему входить в вашу обычную процедуру.

Пока у вас еще работают активные процессы, дайте им закончиться, прежде чем метод OnStop перейдет к уничтожению узла WCF (ServiceHost.Close).

Другой способ - отслеживать активные вызовы, реализуя вашу собственную ссылку counter. вы узнаете, когда можно остановить хост службы,

1
ответ дан 3 December 2019 в 10:26
поделиться

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

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

Надеюсь на это может вам помочь.

0
ответ дан 3 December 2019 в 10:26
поделиться

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

В .NET кажется, что подход к приостановке службы заключается в использовании ServiceController .

0
ответ дан 3 December 2019 в 10:26
поделиться

Проверяет ли эта служба WCF каким-либо образом пользователя? Есть ли у вас метод «рукопожатия»?

0
ответ дан 3 December 2019 в 10:26
поделиться

Может быть, стоит установить

ServiceBehaviorAttribute и атрибут OperationBehavior. Проверьте это на MSDN

0
ответ дан 3 December 2019 в 10:26
поделиться

В дополнение к ответу Мэтью Стиплса

Большинство серьезных балансировщиков нагрузки, таких как F5 и т.д., имеют механизм определения, жив ли узел. В вашем случае, кажется, что он проверяет, открыт ли определенный порт. Но альтернативные способы могут быть легко настроены.

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

.
0
ответ дан 3 December 2019 в 10:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: