2008-12-04 5 views
10

Я пишу службу, которая будет получать вызовы только от локального хоста. Производительность важна, поэтому я решил попробовать NetNamedPipeBinding вместо NetTcpBinding и посмотреть, могу ли я увидеть заметные выигрыши в производительности.Как я могу переписать привязку именной трубы в WCF

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

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

Я знаю, что это подтверждается материалами надежности в NetTcpBinding, но как бы добиться того же уровня надежности повторного подключения в NetNamedPipeBinding? Возможно ли это?

Вопрос несколько академический, поскольку это не требование использования NetNamedPipes, я мог бы так же легко принять его, чтобы использовать привязку tcp, но это зуд, и я бы очень хотел поцарапать его.

+0

IIRC, второй последний параметр CreateNamedPipe (неуправляемая функция win32 под NamedPipeBinding - http://msdn.microsoft.com/en-us/library/windows/desktop/aa365150%28v=vs.85%29.aspx) действует как таймаут соединения с клиентом, который довольно короткий ,Это может иметь какое-то отношение к таймауту, который вы видите при запуске сервера; возможно, вы можете использовать пользовательский рефлектор/dotpeek/отладчик, чтобы увидеть, какие параметры передаются из WCF в нативную функцию, и если эти параметры можно изменить с помощью конфигурации – 2013-01-24 13:40:45

+0

Поскольку вопрос является академическим, в общем, я бы продолжил так: посмотрите, какие нативные функции вызываются, с какими тайм-аутами, а затем трассируются обратно туда, где эти функции вызывается из управляемого кода, чтобы увидеть, откуда возникли параметры. Долго, но весело и помогает вам разобраться, как это работает :) Я отлаживал проблему с Sharepoint таким образом ... – 2013-01-24 13:44:30

ответ

17

Я не использовал NetNamedPipes в WCF, но я потратил больше времени, чем мне было интересно узнать значения таймаута для NetTcp. Я использую следующие конфигурации для своих NetTcpBindings и имел удачу, когда соединение остается активным.

Сервер:

<binding name="MyBindingName" sendTimeout="00:00:30" receiveTimeout="infinite"> 
    <reliableSession enabled="true" inactivityTimeout="00:05:00" ordered="true" /> 
    <security mode="None" /> 
</binding> 

Клиент:

<binding name="MyBindingName" closeTimeout="00:00:30" openTimeout="00:00:30" receiveTimeout="infinite" sendTimeout="00:00:30"> 
    <reliableSession enabled="true" inactivityTimeout="00:01:00" ordered="true" /> 
    <security mode="None" /> 
</binding> 

важные параметры, которые я провел большую часть времени на это в SendTimeout и ReceiveTimeout. Если ваш getTimeout совпадает или меньше вашей отправки, канал будет падать после достижения этого таймаута. Если прием больше, а передача превышает пороговое значение, канал активирует keepalive уровня транспорта. Из моих тестов порог sendTimeout составляет 30 секунд. Что-то меньшее, чем это, и keepalives не отправляются.

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

[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = false)] 
bool KeepAlive(); 

public bool KeepAlive() 
{ 
    return true; 
} 

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

InstanceContext site = new InstanceContext(this); 
_proxy = new MyServiceChannel(site); 
if (_proxy != null) 
{ 
    if (_proxy.Login()) 
    { 
     //Login was successful 
     //Add channel event handlers so we can determine if something goes wrong 
     foreach (IChannel a in site.OutgoingChannels) 
     { 
      a.Opened += Channel_Opened; 
      a.Faulted += Channel_Faulted; 
      a.Closing += Channel_Closing; 
      a.Closed += Channel_Closed; 
     } 
    } 
} 

Надеюсь, что часть этого переводится и имеет ценность для вас с помощью NetNamedPipes.

Edit: Дополнительные параметры для захвата сервер перезапуска вопроса

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

private void Channel_Faulted(object sender, EventArgs e) 
{ 
    IChannel channel = sender as IChannel; 
    if (channel != null) 
    { 
     channel.Abort(); 
     channel.Close(); 
    } 

    //Disable the keep alive timer now that the channel is faulted 
    _keepAliveTimer.Stop(); 

    //The proxy channel should no longer be used 
    AbortProxy(); 

    //Enable the try again timer and attempt to reconnect 
    _reconnectTimer.Start(); 
} 

private void _reconnectTimer_Tick(object sender, System.EventArgs e) 
{ 
    if (_proxy == null) 
    { 
     InstanceContext site = new InstanceContext(this); 
     _proxy = new StateManagerClient(site); 
    } 
    if (_proxy != null) 
    { 
     if (_proxy.Login()) 
     { 
      //The connection is back up 
      _reconnectTimer.Stop(); 
      _keepAliveTimer.Start(); 
     } 
     else 
     { 
      //The channel has likely faulted and the proxy should be destroyed 
      AbortProxy(); 
     } 
    } 
} 

public void AbortProxy() 
{ 
    if (_proxy != null) 
    { 
     _proxy.Abort(); 
     _proxy.Close(); 
     _proxy = null; 
    } 
} 

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

+0

Спасибо Крису, но изменение таймаутов и добавление метода keepalive, к сожалению, решает только первую из моих двух проблем. У меня была бы такая же проблема, когда служба будет перезапущена. – 2008-12-04 17:33:29

+0

Как вы устанавливаете привязку/надежную работу программно? – 2011-08-15 16:29:56

14

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

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

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

Атрибут receiveTimeout, который каждый проверяет, определяет время, в течение которого канал может оставаться бездействующим, прежде чем он будет автоматически отброшен, что говорит о том, что каналы не должны оставаться открытыми очень долго (по умолчанию - 1 минута). Если вы установите для параметра receiveTimeout значение TimeSpan.MaxValue, он будет держать ваш канал открытым дольше, но это не то, что нужно, и то, что вы хотите в практическом сценарии.

Что, наконец, меня на правильном пути был http://msdn.microsoft.com/en-us/library/ms734681.aspx который обеспечивает ужасно глючный пример еще действительно показывает, как нужно идти об использовании ChannelFactory. Ответчики указывают на ошибки и фиксируют запись прямо, поэтому все, что вы можете получить здесь, все, что вам нужно.

А потом все мои проблемы закончились. Нет больше «Операция была предпринята для чего-то, что не является сокетом» и не более «Существующее соединение было принудительно закрыто удаленным хостом».

19

Мой опыт в том, что при использовании NetNamedPipes «ReceiveTimout» для функций привязки, таких как «Тайм-аут неактивности», а не время приема. Обратите внимание, что это отличается от того, как работает NetTCPBinding. С TCP это действительно тайм-аут приема, и есть отдельный тайм-аут бездействия, который вы можете настроить с помощью надежного обмена сообщениями. (Это также противоречит документации SDK, но хорошо).

Чтобы исправить это, установите RecieveTimout на что-то большое, когда вы создаете привязку.
Например, если вы создаете ваши привязки процедурно ...

NetNamedPipeBinding myBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None); 
myBinding.ReceiveTimeout = TimeSpan.MaxValue; 

Или, если вы заботитесь создать ваш связывании декларативно ...

<netNamedPipeBinding> 
    <binding name="myBinding" receiveTimeout="infinite"> 
    </binding> 
</netNamedPipeBinding> 
Смежные вопросы