Я пишу клиент-серверное приложение на С# с использованием WCF. Все мои тесты прошли нормально, но как только я развернул сервис, я заметил случайные проблемы в общении с сервером. Я включил отладку и увидел на сервере такие сообщения:
The communication object, System.ServiceModel.Channels.ServerReliableDuplexSessionChannel, cannot be used for communication because it has been Aborted.
Схема такая:
- клиент отправляет запрос
- сервис обрабатывает запрос
- служба отправляет что-то обратно
- Граница активности на уровне Стоп - вроде все нормально
- Добавьте inactivityTimeout надежного сеанса к дате и времени последнего контакта, и у вас будет метка времени исключения, созданного службой.
Приложение выглядит следующим образом: экземпляр службы предоставляет методы API для взаимодействия с базой данных и имеет тип netTcpBinding. Несколько клиентов (около 40) подключены и случайным образом вызывают методы из сервиса. Клиенты могут оставаться открытыми в течение нескольких дней, даже ничего не отправляя и не получая.
Вот соответствующие биты:
Сервис:
[ServiceContract(CallbackContract = typeof(ISVCCallback), SessionMode = SessionMode.Required)]
[ExceptionMarshallingBehavior]
...
и
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext=true)]
public class SVCService : ISVC
...
Конфигурация службы:
<behaviors>
<serviceBehaviors>
<behavior name="behaviorConfig">
<serviceMetadata httpGetEnabled="false" httpGetUrl="" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceThrottling maxConcurrentCalls="50" maxConcurrentSessions="1000"
maxConcurrentInstances="50" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="tcpBinding" closeTimeout="00:01:00" openTimeout="00:10:00"
receiveTimeout="23:59:59" sendTimeout="00:01:30" transferMode="Buffered"
listenBacklog="1000" maxBufferPoolSize="671088640" maxBufferSize="671088640"
maxConnections="1000" maxReceivedMessageSize="671088640" portSharingEnabled="true">
<readerQuotas maxStringContentLength="671088640" maxArrayLength="671088640"
maxBytesPerRead="671088640" />
<reliableSession inactivityTimeout="23:59:59" enabled="true" />
<security mode="None">
</security>
</binding>
</netTcpBinding>
</bindings>
Конфигурация клиента:
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_ISVC" closeTimeout="00:01:00" openTimeout="00:10:00"
receiveTimeout="23:59:59" sendTimeout="00:01:30" transactionFlow="false"
transferMode="Buffered" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="1000"
maxBufferPoolSize="671088640" maxBufferSize="671088640" maxConnections="1000"
maxReceivedMessageSize="671088640">
<readerQuotas maxStringContentLength="671088640" maxArrayLength="671088640"
maxBytesPerRead="671088640" />
<reliableSession ordered="true" inactivityTimeout="23:59:59"
enabled="true" />
<security mode="None">
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
Что-то здесь не так? Какова наилучшая конфигурация для таких приложений?
Обновлять:
Я столкнулся с одной вещью:
В одном контракте на обслуживание я что-то меняю и уведомляю всех подключенных клиентов. Обычно это работает нормально, по крайней мере, в моих тестах. Но при последнем сбое или зависании я просмотрел журнал и увидел, что в последней функции я использую контракты обратного вызова для уведомления клиентов.
Что я хочу там сделать: я что-то сохраняю в базу данных и в конце уведомляю всех подключенных клиентов об изменении. Я думаю, что список подключенных клиентов уже не актуален и на этом шаге у него истекает время ожидания.
Теперь вопрос, как избежать этих таймаутов.
- Должен ли я использовать многопоточность в службе? Я думаю, что потоки будут уничтожены, как только вызов службы завершится, я здесь?
- Я мог бы реализовать статическую функцию очереди, которая выполняет все уведомления обратного вызова (это то, что предложил Marc_S)
- Есть ли способ надежно обнаружить обрыв соединения внутри сервера?