Это краткое изложение моих комментариев над вопросом.
Я думаю, что в вашем коде есть несколько проблем, которые возникают, когда вы вызываете сервис под нагрузкой.
Ваша декларация WCF ServiceBehavior
.
помечена как
singleton ,
многопоточная служба с поддержкой многопоточности. *1012*
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, // singleton
ConcurrencyMode = ConcurrencyMode.Multiple)] // thread-safe(?)
public class BroadcastorService : IBroadcastorService1 { ... }
InstanceContextMode.Single
прост, так как WCF настраивает это для вас, не имея ничего, что вам действительно нужно сделать однако ConcurrencyMode.Multiple
является декларацией с вашей стороны, что вы можете принимать несколько одновременных вызовов несколькими потоками. Вы заявляете, что принимаете на себя всю ответственность и не несете ответственности за WCF. WCF доверяет вам, что вы не будете стрелять себе в ногу.
Обычно лучше, чтобы WCF определял, как и когда звонки клиентов попадают в код вашей службы. По умолчанию WCF сериализует все вызовы, вызывающие методы в вашем сервисе по одному , что делает ваш сервис поточно-ориентированным , при этом разработчикам не нужно ничего делать со страшными lock()
s. При использовании с InstanceContextMode
это может привести к лучшей масштабируемости вашего сервисного хоста.
Учтите также, что первое, что делает каждый из ваших методов обслуживания WCF, - это выполнение lock()
для всего жестко запрограммированного синглтона, вы не получаете никакой выгоды от ConcurrencyMode.Multiple
. Вы также можете использовать ConcurrencyMode.Single
, удалить все lock()
s и позволить WCF выполнить всю сериализацию вызовов методов за вас. Гораздо безопаснее , чем ручная настройка службы. Плюс, если вы когда-нибудь захотите удалить одноэтапную природу вашего сервиса и использовать, скажем, InstanceContextMode.PerCall
или InstanceContextMode.PerSession
, это, вероятно, изменение в одну строку.
Когда ваше приложение находится под нагрузкой, тот факт, что ваша служба помечена как:
- «Я могу обрабатывать любое количество одновременных вызовов потоков» : P и
- «Пусть все потоки выполняются для одного и того же сервисного объекта» : P
... может привести к очень рискованному обслуживанию. Внося предложенные выше изменения, вы эффективно дросселируете количество одновременных вызовов от всех ваших клиентов , таким образом делая услугу более стабильной .
Сбои, Советы по обработке исключений и отладке
Вы упомянули, что ваше приложение падает, но вы не сказали, в чем была ошибка. Глядя на ваш код, я вижу много:
try
{
// something here
}
catch (Exception ex)
{
}
Как правило, вы хотите избегать подобных действий, потому что вы говорите .NET, что хотите молча перехватить все исключения. Вам, как разработчику, даже не сообщают о возможных неприятных ошибках в вашем коде. Поймать все исключения довольно непослушно , поскольку вы действительно хотите просто поймать те, которые ожидаете, и, возможно, настроить обработчик необработанных исключений для всего остального в вашем коде, который просто отображает фатальное сообщение пользователю перед тем, как закрыть приложение несколько изящно .
Чтобы улучшить отладку, убедитесь, что вы запускаете приложение в отладчике Visual Studio.
В меню Debug выберите Debug.Windows.Exception Settings .
В появившемся окне инструментов установите флажок Исключения общего времени выполнения . Это говорит VS о том, что вы хотите получать информацию о всех CLR исключениях. (позже вы можете войти и просто выбрать те, которые хотите)
Теперь, когда выдается исключение, отладчик останавливается и помещает курсор в строку с ошибкой. Примечание: на данный момент это то, что известно как исключение первого шанса , поскольку отладчик немедленно останавливается на линии.Давайте представим, что TimeoutException
был брошен.Это не обязательно ошибка, как вы могли бы сказать, где-то есть catch (TimeoutException)
.Он еще не перешел к первому catch()
(если есть) блоку, так что не пугайтесь.Если вы нажмете F5 (или Debug.Continue menu), то отладчик возобновит остановку приложения на вашем catch(TimeoutException)
.Если бы вы не поставили галочку в окне «Параметры отладки», отладчик просто перешел бы прямо к вашему catch(TimeoutException)
, не отправляя уведомление с первым шансом.Проблема в том, что теперь вы не знаете, где произошла ошибка, не глядя на стек вызовов в объекте Exception
.
Прокси-клиенты
Хотя, вероятно, это и не непосредственная проблема, я тоже замечаючто ваши клиенты создают прокси WCF и хранят его в поле класса MainWindow
вашего приложения.Суть прокси в том, что через некоторое время они могут сломаться, и WCF не является исключением.Обычно они представляют собой сетевые подключения.Сети приходят и уходят.Соединения могут просто тайм-аут , если простаивает и быть закрыт сервером.Клиент не узнает об этом, пока не отправится вызывать его, к тому времени, когда будет слишком поздно.Вы получите xxxException
, а прокси-сервер будет помечен как неисправен , что означает, что его нельзя использовать снова .Вам нужно будет сделать еще один.
По этой причине, как правило, лучше создать прокси в точке до того, как вы сделаете свой первый вызов, а затем избавиться от него (вы должны Dispose()
это) один раз.текущая партия вызовов завершена.Это или встроить в ваше приложение то, что оно может обрабатывать ошибки WCF и заново создавать прокси-сервер, когда это необходимо.
Теперь, в зависимости от используемой привязки WCF, таймауты могут различаться, это может быть 1, 5или 10 минут.
Опять же, это просто К вашему сведению, я не думаю, что это происходит здесь, но вы никогда не знаете.
Не позволяйте потоку пользовательского интерфейса вызывать службу WCF
OP:
Когда отправка сигнала начинается со стороны администратора на обслуживание, я не могу ничего делать на экране администратора.Даже я не могу свернуть, развернуть или закрыть этот экран
Ваш клиент Admin зависает, потому что вы вызываете службу WCF из обработчика btnSendEvent_Click
.Пользовательский интерфейс не будет ничего делать, пока этот метод не вернется.Это природа всего пользовательского интерфейса. Нет Пользовательский интерфейс является многопоточным.Тот факт, что ваш обработчик кликов выполняет дорогие и своевременные сетевые вызовы, просто сделает ваш пользовательский интерфейс более незаметным.Возможно, вам нужно вызвать его внутри рабочего потока, который предоставляет компонент BackgroundWorker
(новичок проще) или асинхронно через async/await
(лучше).
OP:
Спасибоочень за вашу поддержку.Прямо сейчас я использую BackgroundWorker
в приложении на стороне администратора и применяю изменения в службе WCF согласно вашей инструкции.Теперь отправляет сигнал плавно . без сбоев и зависаний приложения на стороне администратора.
Я рад слышать, что мое предложение помогло решить проблему.
Расскажите подробнее
Я очень рекомендую эту превосходную библию WCF в любой книге / форме Kindle: