Я боролся с WCF Proxies. Каков правильный способ утилизации прокси-сервера WCF? Ответ не является тривиальным.Что такое правильный способ утилизации прокси-сервера WCF?
System.ServiceModel.ClientBase нарушает Microsoft собственного Dispose-модель
делает реализацию IDisposable
поэтому необходимо предположить, что его следует утилизировать или использовать в using
-блоке. Это лучшие практики для чего-то одноразового использования. Однако реализация является явной, поэтому нужно явно указать ClientBase
экземпляры на IDisposable
, помутняя проблему.
Самым большим источником путаницы, однако, что вызов Dispose()
на ClientBase
экземпляров, FAULTED, даже каналы, которые FAULTED, потому что они никогда не открывал в первую очередь, приведет исключение броска. Это неизбежно означает, что осмысленное исключение, объясняющее ошибку, сразу же теряется, когда пакет распадается, область using
заканчивается и Dispose()
бросает бессмысленное исключение, говоря, что вы не можете уничтожить неисправный канал.
Вышеприведенное поведение является анафемой . Расположите шаблон, в котором говорится, что объекты должны быть толерантными к нескольким явным обращениям к Dispose()
. (См http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx, «... позволяют Dispose(bool)
метод, который будет вызван более чем один раз. Метод может выбрать не делать ничего после первого звонка.»)
С появлением инверсии из-под контроля , эта плохая реализация становится реальной проблемой. I.O.C. контейнеры (в частности, Ninject) обнаруживают интерфейс IDisposable
и вызывают Dispose()
явно активированные экземпляры в конце области впрыска.
Решение: Proxy ClientBase и переадресация вызовов Dispose()
Мое решение было прокси ClientBase
на подклассы System.Runtime.Remoting.Proxies.RealProxy
и угона или перехватывать вызовы на Dispose()
. Моя первая замена Dispose()
пошел что-то вроде этого:
if (_client.State == CommunicationState.Faulted) _client.Abort();
else ((IDisposable)_client).Dispose();
(Обратите внимание, что _client
является ссылкой типизированных к цели прокси-сервера.)
Проблемы с NetTcpBinding
Я думал, что это сначала прибила его, но потом я обнаружил проблему в производстве: при определенных сценариях, которые яростно трудно воспроизвести, я обнаружил, что каналы, использующие NetTcpBinding
, не закрывались должным образом в случайном случае, хотя Dispose
вызывается на _client
.
У меня было приложение ASP.NET MVC с использованием моей реализации прокси для подключения к службе WCF с использованием NetTcpBinding
в локальной сети, размещенной в службе Windows NT в кластере служб с одним узлом. Когда я тестировал приложение MVC, некоторые конечные точки службы WCF (которая использовала совместное использование портов) перестали отвечать через некоторое время.
Я изо всех сил пытался воспроизвести это: те же компоненты, работающие по локальной сети между двумя машинами разработчика, отлично работали; консольное приложение, забивающее реальные конечные точки WCF (работающие на промежуточном сервисном кластере) со многими процессами и многими потоками в каждом из них; настройка приложения MVC на промежуточном сервере для подключения к конечным точкам на машине разработчика, работающей под нагрузкой; запуск приложения MVC на машине разработчика и подключение к промежуточным конечным точкам WCF. Однако последний сценарий работал только в IIS Express, и это был прорыв. Конечные точки будут зацикливаться при тестировании нагрузки приложения MVC в полножировом IIS на машине разработчика, подключаясь к кластеру промежуточных сервисов.
Решение: Закройте канал
После неудачной попытки понять проблему и читать много, много страниц MSDN и других источников, которые утверждали, что проблема не существует вообще, я попробовал длинный выстрел и изменили мою Dispose()
обходных к ...
if (_client.State == CommunicationState.Faulted) _client.Abort();
else if (_client.State == CommunicationState.Opened)
{
((IContextChannel)_client.Channel).Close();
((IDisposable)_client).Dispose();
}
else ((IDisposable)_client).Dispose();
... и проблема прекратила происходить во всех тестовых установках и под нагрузкой в промежуточной среде!
Почему?
Может ли кто-нибудь объяснить, что могло произойти, и почему явное закрытие Channel
перед вызовом Dispose()
решило его? Насколько я могу судить, это не обязательно.
Наконец, я возвращаюсь к первому вопросу: Каков правильный способ утилизации прокси-сервера WCF? Является ли моя замена для Dispose()
адекватной?