×
Ви дійсно бажаєте відкрити доступ до тестування за курсом WCF Essential на 40 днів?
ВІДЕОУРОК № 4. Уровни экземпляров сервисов. Каналы и привязки.
Видео курс WCF Essential
Урок 4. Уровни экземпляров сервисов. Каналы и привязки
Здравствуйте. Меня зовут Александр Шевчук. Я сертифицированный тренер Microsoft, работаю в компании CyberBionic Systematics. Я рад Вас приветствовать на информационном видео ресурсе для разработчиков программного обеспечения ITVDN. Тема данного урока «Уровни экземпляров сервисов».
Давайте перейдем к следующему слайду и посмотрим, какие же уровни экземпляров сервисов у нас бывают. Мы видим, что WCF поддерживает три типа активизации экземпляров. То есть у нас бывает три разновидности экземпляров сервисов провайдеров.
Первый – это службы, или сервисы, уровня сеанса. То есть мы видим, что здесь создается один экземпляр службы для каждого подключения, то есть для каждого сервиса consumer. Давайте посмотрим на следующий слайд. Мы видим, что к нашему сервис провайдеру подключается несколько консьюмеров. И для каждого консьюмера будет созданная своя версия, так сказать, экземпляр класса сервис. И здесь, между ними, формируется некий сеанс и вот этот экземпляр живет указанное количество времени, например 10 минут, 15 минут, то время, которое мы укажем в настройках, если мы будем задавать какие-то специфические настройки. И если консьюмер в течение этого времени не обращается, к этому экземпляру, то система его удаляет. И при повторном обращении придется создавать новый экземпляр. Ну а так, если консьюмер периодически к нему обращается, то соответственно этот экземпляр будет жить долго, ну а точнее определенное количество времени, указанное количество времени, после последнего обращения к нему.
Следующая разновидность экземпляров – это службы уровня вызова. Обратите внимание, для каждого клиентского запроса создается (и в последствии уничтожается) новый экземпляр службы. Вот, давайте посмотрим. Обратите внимание, у нас снова же имеются консьюмеры, и когда мы посылаем запросы, то, обратите внимание, каждый новый запрос, к сервису, создает его новый экземпляр, а вот эти отмирают. Даже если Вы в цикле будите вызывать на экземпляре один и тот же метод, вот представьте, что здесь у Вас есть цикл, который, допустим, 4 раза вызывает один и тот же метод, то на каждый вызов метода будет создаваться новый экземпляр класса сервиса на стороне клиентов, а вот эти будут отмирать, и будет каждый раз создаваться новый для каждого. В каких-то случаях и такой подход является эффективным, мы будем в дальнейшем рассматривать.
Следующая разновидность экземпляров – это синглетные службы. Все подключения клиентов обслуживаются только одним экземпляром. Вот, давайте посмотрим. У нас имеется один экземпляр класса сервис, он является синглтоном, то есть одиночкой. И все сервисы консьюмеры, которые посылают сообщения на сторону сервис провайдера, они будут ожидать других, потому что их сообщения здесь становятся в очередь, и, допустим, первый консьюмер прислал сообщение, а вот этот его 2 минуты обрабатывает, и эти теперь шлют сообщения. И вот, сообщения вот этих консьюмеров, которые вот ниже, станут в очередь и будут ожидать, пока сервис провайдер не обслужит первого сервис консьюмера. И дальше, когда он обслужит, ответит, он берет из очереди следующий запрос и начинает обслуживать его. В каких-то случаях и такой подход оказывается полезным.
И я предлагаю перейти к рассмотрению программных кодов, примеров создания экземпляров различных уровней. Давайте еще раз повторим, какие бывают уровни. Это службы уровня сеанса, службы уровня вызова и синглетные службы.
А мы переходим к рассмотрению примеров. Откроем Visual Studio. И давайте сначала посмотрим на имеющиеся контракты. Обратите внимание, у нас имеется контракт с именем IContract. В теле контракта имеется один абстрактный метод, который возвращает строковое значение. Зайдем на сторону сервиса. На 13 строке мы создаем класс MyService, который реализует интерфейс IContact и интерфейс IDisposable. Ну, в данном случае в IDisposable у нас просто выводится на экран хэш-код нашего экземпляра. Зачем? Потому, что при финализации, при завершении работы с сервисом будет вызван метод IDisposable, и мы можем увидеть, что именно хэш-код того экземпляра, который был создан и сейчас удаляется. А в методе мы просто возвращаем, снова же, на сторону клиента хэш созданого экземпляра. Зачем? Ну за чем, что бы посмотреть работу служб уровня вызова, потому что мы с Вами помним… давайте перейдем в презентацию… мы с Вами помним, что на каждый вызов будут создаваться новые экземпляры сервиса. И обратите внимание, на 12 строке мы сервис помечаем атрибутом ServiceBehavior. И вот комментарий – применяя атрибут ServiceBehavior, со свойством InstanceContextMode равным PerCall, служба помечается как служба уровня вызова. И теперь, видите, мы пометили этим атрибутом класс сервиса, и теперь WCF будет создавать его экземпляры каждый раз, и уничтожать его экземпляры каждый раз, когда отработает даже его единственный вызов метода.
Давайте теперь посмотрим на сторону хостинга. Тут у нас создается хост, добавляется конечная точка, открываем. Ничего необычного, все знакомо. Единственное, обратите внимание на 8 строку. В данном случае будет проблема выполнения этой программы через студию, через F5 или Ctrl F5. И поэтому, мы будем пробовать запускать непосредственно сгенерированные исполняемые файлы.
Заходим на сторону клиента и смотрим. Создаем фабрику каналов, создаем канал. И вот на канале мы в цикле 10 раз вызываем метод. И давайте мы проверим, что действительно у нас будет проблема выполнения через Visual Studio. Я вот нажимаю Ctrl F5, даже не через отладку. Вот у нас имеется сервер. Видите, у нас появилась ошибка. Поэтому, мы сейчас с Вами зайдем в файловый менеджер, зайдем в сервис, запустим его от имени администратора. И сейчас запустим клиентский сервис, тоже на всякий случай от имени администратора. И, обратите внимание, сейчас у нас здесь пойдут вызовы. Видите, да. Обратите внимание, мы в цикле из клиента каждый раз обращались к серверу, казалось бы, на одном канале вызывая один и тот же метод. Вот, посмотрите, в клиенте. Видите, да. Создали канал и на одном канале мы вызываем метод, раз за разом получая хэш-коды. Вот видите, хэш-код текущего экземпляра сервиса. И, что мы видим в итоге? Мы видим, что на сервере закрываются объекты, вызывался метод dispose автоматически. И вот мы выводили их хэш-коды. Мы понимаем, что хэш-код объекта это как номер паспорта человека, он не может совпадать. Поэтому мы видим, что здесь создавались каждый раз новые экземпляры класса MyService. Каждый вызов заставлял систему создавать новые экземпляры, мы это видим по хэш-кодам.
Рассмотрим сейчас еще один пример уровня PerCall. Зайдем в Visual Studio и посмотрим на контракт. Мы видим, что контракт не изменился, но хочется сначала прочитать комментарий – служба уровня вызова должна быть с контролем состояния (state-aware), состояние службы бывает высокозатратным, поэтому экземпляр службы требуется уничтожать после каждого вызова. Ага. Давайте зайдем в сервис MyService и посмотрим. Мы на 14 строке создаем экземпляр класса MyService, помечаем его атрибутом ServiceBehavior и указываем значение свойства InstanceContextMode равное PerCall. То есть мы создаем сервис уровня вызова. Реализуем IDisposable. И в теле метода MyMethod мы обращаемся к какому-то еще методу, а здесь мы используем какое-то состояние. Давайте перейдем и посмотрим, что это за состояние. Вы скажите «Ага, здесь идет очень большое состояние». На 11 строке мы создаем массив элементов типа int размерностью в один миллион элементов. Если мы один миллион умножим на 4, то получим 4 миллиона байт, а это примерно 4 мегабайта, условно округлим. А если еще ноль добавим, то будет 40 мегабайт. И будет не очень хорошо, если такие крупные объекты будут долго, на протяжении длительного периода времени висеть в памяти. Поэтому, скорее всего, есть смысл организовать работу так, что бы с объектами, у которых имеется такой высоко затратное состояние, мы работали как со службами уровня вызова, что бы мы ни держали их долго в памяти. Перейдем в хост. Хост не изменился. Client, Client тоже не изменился. Мы просто посмотрели, уже идеологически, а зачем же нам может понадобиться использование сервисов уровня вызова. То есть вот, это одна из причин. Плюс ко всему, хочется остановиться немножко на реализации интерфейса IDisposable. Представьте себе, что Вы используете неуправляемые ресурсы. То есть Вам досталась библиотека, написанная на С++, или Вы ее приобрели, и строите экземпляры классов в неуправляемой куче. И получается, что сервисы, вот сервис PerCall, будет каждый раз удаляться. Он будет строить такой огромный экземпляр и удаляться. Стоить и удаляться. А при работе с неуправляемой кучей Вам приходится вручную удалять экземпляры с той кучи. Поэтому, пожалуйста, предусмотрите такую возможность удаления и финализации неуправляемых ресурсов в методе Dispose, реализация интерфейса IDisposable, и как мы видим, уже из контекста этого примера, что этот метод вызывается автоматически исполняющей средой. Пример очень похож на предыдущий, поэтому Вы можете его выполнить и посмотреть. Только не забывайте выполнять исполняемые файлы, потому что со студии, при выполнении непосредственно из студии, F5 или Ctrl F5, Вы не получите ожидаемо результата и может даже подумаете, что Вы где-то совершили ошибку, которая не позволяет Вам выполнится. Поэтому не пугайтесь и сначала перепроверьте, а может .exe запускается, если я все правильно написал. А если уже и .exe не запускаются, то тогда уже смотрите настройки App.config, может Вы с конечными точками что-то напутали, или с определенными свойствами, которых в WCF очень большое множество.
А мы переходим к рассмотрению следующего примера. Заходим в Visual Studio и смотрим. Заходим в Contract. Контракт у нас не изменился. Заходим на сторону сервиса. И давайте посмотрим. На 13 строке мы создаем класс MyService, который реализует интерфейсы IContract и IDisposable, уже все это было. Реализация того же метода Dispose. Все один в один. Те же хэш-коды возвращаются. Единственная разница между этим примером и предыдущем в том, что мы в атрибуте ServiceBehavior свойству InstanceContextMode присваиваем значение PerSession. Так вот, мы понимаем, что в данном случае у нас организуется сессия, то есть сеанс. Давайте мы зайдем в хост. Смотрим, в хосте у нас указывается timeout 10 минут, наш сеанс будет существовать 10 минут с последнего обращения к сервису. Вот если я, консьюмер, послал запрос, получил ответ, и 10 минут не посылаем никаких запросов, то среда WCF автоматически удалит этот сервис. И если на 11 минуте потом я отсылаю запрос, то получается, что для меня будет создаваться новый сервис. Вы скажите «А если в сервисе будет сконфигурирован какие-то поля, вот находятся какие-то значения». Ну, я как программист знаю, значит должен предусмотреть эти моменты и, допустим, сделать большую задержку, или наладить внутренние процессы, как-то предусмотреть, организовать какую-то персистентность, то есть что бы эти объекты как-то сохраняли свое состояние. Это уже дело логики.
Далее заходим в клиент, видим, что на стороне клиента тоже самое, мы указываем ReliableSession.InactivityTimeout на 10 минут. И здесь у нас происходят вызовы... создали канал… и на канале вызываем 10 раз метод. И смотрите, здесь мы можем выполниться даже через F5. То есть выполнять исполняемые файлы, то, что мы делали до этого, требуется лишь тогда, когда у нас этот атрибут равен PerCall. Ясно? Если он равен чему-то другому, то нам уже не надо выполнять через исполняемые файлы, мы можем стандартно взять, нажать клавишу F5 и в стандартной отладке выполнить нашу программу, в режиме отладки. Обратите внимание, все вызовы методов, сейчас мы зайдем в клиент, видите, все вызовы метода MyMethod на сервисе вернули нам один и тот же хэш-код. Это доказывает то, что мы сейчас работаем с сервисом уровня PerSession, что у нас сформирована сессия. Если бы мы сейчас подождали 10 минут, но мы не будем ждать 10 минут, и еще раз обратились к этому сервису, то мы увидим, что у нас уже выведутся другие хэш-коды, потому что сгенерируется новый экземпляр и сформируется новый сеанс, то есть новая сессия. Думаю, этот пример тоже достаточно прост для понимания.
И мы с Вами переходим к рассмотрению следующего примера. Зайдем в Visual Studio и посмотрим, традиционно, в контракт. Контракт не поменялся. Можно посмотреть, хостинг теперь самый обычный, мы уже не ставим здесь никаких timeout. И в клиенте тоже все просто, без timeout. Вы скажите «Скорее всего, мы подходим к службе уровня singleton». Есть такой даже паттерн. В каталоге GoF называется singleton, это значит, что этот паттерн говорит о том, что этот объект, объект определенного типа, может быть в системе только один. Ну, там конечно имеются частные случаи синглтонов, когда может быть одновременно два экземпляра, три, определенное количество. Мы будет вот такой стандартный, общий подход, только один экземпляр класса в системе. И заходим в сервис. На 12 строке мы создаем класс MyService, та же реализация двух интерфейсов. И на 11 строке помечаем сервис атрибутом ServiceBehavior и задаем его свойство InstanceContextMode вот такое значение Single. Теперь хотелось бы выполниться. Давайте попробуем выполниться через F5. Вот, мы обратились к серверу. И смотрим, что на стороне сервера у нас имеется один и тот же экземпляр. Вы скажите «Ага, а вдруг это сессия? Вдруг он не синглетный?». Хорошо, придется проверить. Сноваже, open folder in file explorer. Заходим на сторону сервиса. Запускаем его от имени администратора. Вот заметьте, я запустил этот сервис и сейчас запущу несколько экземпляров клиента. И смотрим, вот у нас имеется один клиент, вот у нас имеется второй клиент, вот у нас имеется третий клиент. Обратите внимание, все эти три клиента обращались к одному сервису. И смотрите, что сервер им возвращал? Хэш-код экземпляра класса MyService. И мы видим, что этот пример действительно доказывает, что на стороне сервера работает singleton, который работает по вот такой схеме. Все сообщения становятся в очередь и обслуживаются поочередно.
А мы с Вами идем дальше и рассмотрим еще одну важную особенность, которая называется демаркационные операции, то есть мы посмотрим, как налаживать определенный порядок вызова операций. Мы видим на этом слайде контракт, помечаем его как SessionMode = SessionMode.Required, это значит, что демаркационные операции могут использоваться только в режиме сессии, PerSession. Теперь, когда мы будем реализовывать этот контракт, то сервис обязательно должен быть уровня PerSession. Вот такой. Ни синглетный, ни уровня вызова, а только PerSession, только в этом случае мы сможем использовать демаркационные операции. То есть мы видим, что демаркационные операции представляют собой определенный порядок их вызова. И, мы понимаем, что одни операции должны вызываться первыми, например, First, IsInitiating = true. Другие не должны вызываться первыми, Middle не должна вызываться первой и она не должна быть последней, IsTerminating (завершающая) = false. А какие-то операции обязательно должны быть вызваны последними. И мы видим, что First должна быть вызвана первая клиентом, потом Middle, потом Last. Ну, насколько хороша такая автоматность, потому что здесь уже прослеживается некая автоматность, из области конечных автоматов. Насколько это хорошо в сервис-ориентированном программировании… ну, стоит задуматься.
И давайте сейчас перейдем к примеру программного кода и посмотрим, как же работают демаркационные операции. Заходим в Visual Studio и смотрим, традиционно с контракта. На 12 строке мы создаем контракт, помечаем его атрибутом ServiceContract и указываем значение свойства SessionMode равное Required. Обязательно должен быть уровень PerSession, уровень сессии. Вот с презентации три наши метода – First, Middle, Last. Мы видим, что First должен вызываться первым, Middle уже где-то посередине, сколько угодно раз, а вот с IsTerminating=true (Last) должен вызваться последним.
Заходим в MyService. На 14 строке создаем класс MyService и реализуем в нем IContract и IDisposable интерфейсы. Вот у нас имеется реализация методов, каждый метод выводит на экран строку, оттеняя свою работу, и возвращает клиенту свое имя. Что бы клиент видел, что они действительно выполнились в такой последовательности. И Dispose грохает этот объект. Заходим на сторону хоста. Да, и еще, посмотрите, пожалуйста, что в MyService у нас идет уровень PerSession. Потому что если мы попробуем выбрать какой-то другой уровень, то у нас не сработает этот код. Почему именно PerSession? Потому что по-другому это просто бессмысленно. Представьте, PerCall. Вы на экземпляре сервиса уровня PerCall можете вызвать только один раз метод. На синглтоне Вы можете вызывать много раз методы, но тогда другие сервисы тоже запутаются, потому что странная логика, правда? Поэтому разрешено использовать такие демаркационные операции только при создании экземпляров сервисов уровня PerSession, потому что иначе просто бессмысленно. И давайте выполнимся и посмотрим. Тут у нас имеется сервер и имеется клиент. Вот, пожалуйста, у нас произошли вызовы, далее у нас произошла ошибка. Почему? Давайте посмотрим. Потому что мы пытались, скорее всего, вызвать на стороне клиента… вот мы задаем timeout, создаем канал, и вот мы вызываем First, Middle, Last, все правильно. И теперь, на 29 строке, мы пытаемся вызвать недопустимую операцию First, а ее нельзя вызвать. Вот поэтому у нас вот здесь и появилась ошибка. Говорит «This channel cannot send any more messages because IsTerminating operation “Last”…» уже была вызвана, канал не может больше принимать никаких сообщений, потому что операция, помеченная как IsTerminating, уже была выполнена. Так же мы не сможем дважды что-то вызывать. Допустим, вызвать Last или Middle. Хорошо. А мы с Вами идем дальше.
И сейчас мы перейдем к рассмотрению таких понятий, как каналы и привязки. Мы с Вами знаем, что канал – это «труба», по которой курсируют все сообщения от сервиса консьюмера к сервис провайдеру, он принимает, открывает это сообщение, как-то обрабатывает его, выполняет, формирует новое сообщение и отправляет на сторону консьюмера, консьюмер открывает этот конверт и выводит нам на экран возвращаемое значение методов выполненных на сервис провайдере. Но канал так же еще отвечает и за подготовку сообщений, и доставку этих сообщений неким таким, я бы сказал, единообразным способом. А хотелось бы посмотреть, как же выглядит канал физически. Хотя бы немножко. Что же, какие же механизмы в этой зеленой трубе?
Давайте перейдем к следующему слайду и посмотрим. Ага, вот мы здесь видим такое понятие, как стек каналов или комбинация каналов. То есть у нас канал состоит из вот таких более мелких каналов, трубочек. И мы видим, что большой канал состоит из протокольного канала, из транспортного канала, это два таких основных канала (протокольный и транспортный). Что это такое, мы сейчас рассмотрим. И прочие каналы, мы их будем рассматривать дальше на WCF Advanced, уже более детально изучать каналы. И главное назначение вот этого стека каналов состоит в том, что бы преобразовывать передаваемые по сети сообщения в тот формат, который совместим и с получателем, и отправителем. А также организовывать транспортировку сообщения, то есть мы знаем, что есть такое понятие как протокол и церемониал, вот это как раз и есть протокольный канал. Протокол – это, так сказать, формат по которому мы общаемся. Ну, протокол – как правильно нам общаться, как правильно обрабатывать эти сообщения. И так же нам требуется организовать транспортировку этого канала, то есть, на каком транспорте мы будем их везти. То ли на метро, то ли на такси, то ли на tcp, то ли на http, то ли на ipc, то есть какой транспорт мы будем использовать. Запомнили, протокол, как в дипломатии, протокол и церемониал. Как мы его доставляем, как мы общаемся друг с другом. Это чем, чем мы доставляем. И для этого используются вот такие каналы двух видов – транспортный канал и протокольный канал. Все остальные каналы мы будем рассматривать позже, они уже такие, немножко специфические. И вот заметьте, что транспортный канал, он всегда находится внизу стека каналов и отвечает за транспортировку сообщения в соответствие именно с транспортным протоколом. А протокольные каналы, они всегда располагаются поверх транспортных, и вообще поверх всех других каналов, и именно эти каналы, протокольные каналы, трансформируют и как-то модифицируют, изменяют сообщения. Так вот, главное понимать, что мы еще на самом первом уроке смотрели архитектуру, общую архитектуру, она разделялась, условно, на две части. И можно сказать, что здесь у нас идет протокольный уровень, а здесь идет транспортный уровень. Протокол – это такой, более человеческий, вот то, что мы выводили сообщения в xml формате, в SOAP формате, вот можно сказать, что этот формат представляет собой протокольный формат. А транспортный канал это уже, так сказать, более техничный канал, мы уже смотрим, как это сообщение будет доставляться.
И переходим к следующему слайду и смотрим. Теперь мы видим, что на самом деле все вот эти трубочки большого основного канала являются так называемыми элементами привязки. Обратите внимание, главное правило – что именно привязка, binding, описывает конфигурацию стека канала. Вот этот канал описывается именно привязкой. Мы пока использовали привязки по умолчанию, просто по умолчанию, но мы можем создавать и свои собственные пользовательские привязки и формировать всякие самые сложные каналы, выбирать из набора трубочки и формировать наш собственный канал. То есть мы видим, что каждая привязка составляется из наборов элементов привязки. И мы будем канал, целый канал, ассоциировать с чем? С привязкой. А что делает канал? Канал у нас привязывает сервиса консьюмера к сервису провайдеру. То есть каждая привязка составляется из набора элементов привязки, который представляет отдельные маленькие каналы-стеки.
И переходим к следующему слайду. Здесь у нас как раз и происходит такое описание – привязкой называется заранее сконфигурированный стек каналов. В привязке задается способ транспортировки сообщения, способ кодирования сообщения и протоколы, участвующие в коммуникациях. То есть как раз вот эти прочие каналы и могут отвечать за что? Вы скажите «за кодирование, за разные специфические операции, которые мы можем выполнить над сообщением».
Давайте посмотрим еще раз, какие у нас бывают основные привязки. То есть привязка, как некий набор настроек, который относится к транспортному протоколу кодирования сообщений, коммуникационной схеме, как мы будем связываться, надежности, безопасности, к тем же транзакциям, совместимости сервисов, все, что мы рассматривали в самом начале курса. Если Вы забыли – посмотрите пожалуйста. Здесь показаны 9 стандартных привязок. Привязок немножко больше, ну мы не будем на самом стартовом курсе по WCF рассматривать какие-то специфические привязки, потому что мы с Вами понимаем – мы хотим легко, в простой манере понять работу WCF. И мы видим, что каждая привязка имеет определенные кодировки, описывает кодировки. Вы скажите «Да, я понимаю, что все кодировки описываются прочими каналами». Здесь имеется транспортный префикс, внутри каждой привязки. Вы скажите «Я понимаю, что это уже вот он, транспортный префикс, который все это задает». Ну и соответственно, как показывает этот слайд, 9 основных привязок. С какими-то мы с Вами уже были знакомы, с BasicHttpBindind, мы видим, что здесь кодирование происходит при помощи Text кодировки, MTOM. Чуть попозже мы разберем способы кодирования сообщений, и снова же в простой манере посмотрим, что же они представляют собой и как они используются.
А мы с Вами перейдем сейчас к рассмотрению, снова же, использования привязок в примерах. Откроем Visual Studio и посмотрим. В этом примере мы на 13 строке коллекцию элементов типа Binding. Мы понимаем, что все привязки в итоге, наверно, наследуются от Binding, если так происходит, потому что мы в коллекцию добавляем экземпляры конкретных привязок. А мы же понимаем, что это и есть, на самом деле, вот эти зеленные трубы. Давайте сделаем go to definition в самую простейшую привязку. Смотрим. Ага, на 11 строке класс BasicHttpBinding наследуется от HttpBindingBase. Хорошо, заходим сюда. И мы видим, что этот класс, HttpBindingBase, наследуется от базового класса Binding, от базовой привязки. Делаем go to definition в Binding. Здесь уже нету никакого наследования, только реализация вот такого технического интерфейса. Давайте вернемся в код. Смотрим, мы набираем целую коллекцию разнообразных привязок. Зачем? Сейчас посмотрим. Для того, что бы посмотреть все их элементы. Мы берем сейчас все трубы и хотим посмотреть, как вот здесь, мы берем все зеленые трубы, и сейчас будем смотреть, какие же элементы имеются в каждой этой трубе. Здесь поможет нам вспомогательный метод ShowBindingElements с 33 строки. Мы передаем сюда весь наш набор, на 35 строке в цикле foreach из коллекции привязок мы поочередно извлекаем привязки, помещая в переменную итерации binding, на 37 строке и на 39 строке мы зеленым подсвечиваем имя привязки, а далее в цикле foreach мы говорим «Привязка, дай нам, пожалуйста, все свои элементы, из которых ты состоишь», и выводим их имена на экран. Давайте посмотрим. Вот, обратите внимание. Вот Showing Binding Elements for BasicHttpBinding. Она состоит только из двух элементов – TextMessageEncodingBindingElement и HttpTransportBindingElement. Видите, у нас идет только кодировка сообщений, текстовая кодировка сообщений. Мы говорим «Гоним чистый текст и едим http транспорт». То есть, все сообщения передаются по http протоколу, по самому нижнему каналу. Давайте посмотрим еще какую-то. Ага, NetNamedPipeBinding. Вы скажите «Ага, эта схема IPC использующая именованные каналы Windows». И здесь, снова же, идут те элементы, из которых состоит эта привязка. Видите, те, уже серые трубки. И мы видим, что самая простейшая привязка, BasicHttpBinding, она состоит только из двух трубок. У каких-то может быть больше трубок, у каких-то две трубки. Все зависит от нас, какие мы привязки хотим создать. Далее, NetTcpBinding. Мы говорим «Ага, транспорт tcp используется». TransactionFloxBindingElement, BinaryMessageEncodingBindingElement (бинарная кодировка будет идти), WindowsSecurityBindingElement (здесь уже и безопасность работает, на уровне tcp). Смотрим, NetMsmqBinding – BinaryMessageEncodingBindingElement (бинарная кодировка). Это аналог бинарной сериализация. Когда Вы изучали тему сериализации, на professional, Вы наверно знаете, что у нас какие бывают формы сериализации? Это бинарная сериализация, xml сериализация и еще одна разновидность сериализации – SOAP сериализация, которая и позволяет как раз генерировать, сериализовать, с учетом протокола SOAP, но она не используется. Почему? Потому что в стандартной сериализации уже не поддерживаются новые версии протокола SOAP, поэтому Microsoft и не рекомендует пользоваться стандартной SOAP сериализацией. Так вот, здесь у нас идет обычная бинарная сериализация. И мы помним, что если мы сериализуем объект через binary форматор, то десериализовать его можно только под Windows, только под .NET. Уже передавая на другие платформы никто не может его никак десериализовать, потому что никто не поддерживает напрямую Microsoft формат. А если мы в xml формате сериализуем, то его могут десиреализоть и на других платформах. А BinaryMessageEncodingBindingElement, так как мы здесь пользуемся электронной почтой для сообщений, мы с Вами говорили, Microsoft Message Queuing, это технология, которая представляет собой некую электронную почту не для людей, а для сообщений, мы сообщение кодируем в бинарном виде и пользуемся их собственным транспортом. Ага, пиринговые сети, различные файлообменники. Из каких же трубок они состоят? Они из трех трубок состоят. У них свой специфический транспорт, бинарная кодировка. Здесь уже не буду уточнять, что это за кодировки, потому что мы пока еще с ними не знакомы. Скоро познакомимся с форматами сообщений и дальше Вам вот эти элементы привязки будут ясны более подробно. Ну, конечно, мы здесь пытаемся немножко углубится, но ничего страшного. Смотрим, WS поддерживает Web service star стандарт, для dual channel, смотрите, сколько для организации дуплексного канала имеется привязок. А вот тот же http binding, не basic, а тот, который придерживается спецификации web service specifications, у него уже 4, он уже беспокоится о безопасности сообщений. Федеративные привязки. Видите. Федеративные привязки, эти привязки используются, когда мы, например, хотим получить… например, когда мы работаем с active directory, когда работаем с такими системами, допустим, аутентификации, такими как single sign on, об этом Вы уже будите, это Вы уже узнаете на отдельных уроках курса advanced, когда уже будите рассматривать элементы безопасности и элементы windows identity foundation. Вот также MsmqIntegrationBinding, состоит из одного интеграционного элемента, который, скорее всего, оборачивает в себе какие-то другие элементы. Вот мы, каким простым способом посмотрели, как можно узнать какой канал, какая привязка из каких трубочек, то есть из каких элементов состоит.
А мы с Вами переходим дальше к рассмотрению следующего примера. Зайдем в Visual Studio и посмотрим еще один пример того, как мы можем настраивать привязки. Заходим в сервис. Самая простейшая реализация, метод возвращает хэш-код сервиса. Контракт, самый простейший метод. Вот зайдем на сторону хоста. Когда мы создаем сервис хост и создаем привязку, обратите внимание, мы здесь создаем TCP привязку, говорим, что все сообщения будут обмениваться по TCP транспортному протоколу. А TCP протокол, он тоже настраивается. Мы говорим, что время ожидания закрытия соединения между… потому что если мы не указали в сервисе, какой уровень этого сервиса, толи это PerCall, PerSession, синглетный, мы понимаем, что по умолчанию он будет PerSession, то есть уровня сеанса. Поэтому, мы говорим, что ждем максимально долго. Далее, способ сравнения имен узлов при разборе URI (Uniform Resource Identifier). Далее, число каналов, готовых к обслуживанию запросов, все последующие запросы становятся в очередь. Вы скажите «Ага, вот это как раз и есть твоя разновидность синглтона, который ты говорил, что есть такие синглтоны, описываются в каталоге паттерна, что он может, допустим, содержать в себе ограниченное число, четко указанное». Я говорю «Вот, в системе может быть только 3 экземпляра такого-то класса». Это тоже синглтон, несмотря на то, что он не один, а их несколько. Это такая разновидность синглтона. Далее, размер пула буферов для транспортного протокола. Число входящих или исходящих соединений, то есть все входящие и исходящие соединения будут считаться порознь. Далее, размер одного входящего сообщения, то есть вот такого размера 65536 байт – размер сообщения. Имя привязки, мы хотим привязке дать friendly name, что бы можно было к тебе как-то обращаться из каких-то других мест. Далее, время ожидания завершения операции открытия соединения, сколько мы ждем, пока откроется соединение. Вот, две минуты, потому что вдруг там происходят тоже проверки, мало ли что может быть, какие-то задержки. Разрешение/запрет обобщения портов слушателями служб, может ли быть обобщен общем. Время ожидания завершения операции приема, сколько мы ожидаем. Ну и дальше у нас уже добавляется конечная точка и открывается хост. Если Вам здесь какие-то свойства непонятны – ничего страшного, дальше по курсы мы их будем рассматривать, потому что этот пример призван Вам показать, что смотрите, мы создаем экземпляр существующей привязки, вот этого зеленого канала, и можем дальше его еще настраивать. Поэтому не спешите собирать свои собственные каналы, попробуйте настроить существующие, возможно они Вам подойдут. Мы не можем углубиться сейчас в каждый пункт, посмотреть более детально, но на протяжении курса мы рассмотрим. Поэтому смотрите на этот пример просто, как на пример того, что каким-то определенным образом можно настроить привязку. Мы сейчас не можем все полностью осознать в настройке привязки, потому что для нас пока это вот такая метафора, которая состоит из зеленой трубы с трубками. Ну, не смотря на это, мы уже начинаем подходить к пониманию, подойдем дальше к более техническим моментам. Сервис мы видели, контракт видели. Смотрим на сторону клиента. В клиенте используется стандартная NetTcpBinding и эти каналы, они уже согласуются друг с другом, когда будет происходить соединение, привязка клиентская и привязка серверная.
Мы с Вами перейдем к еще одному примеру. Заходим в Visual Studio и смотрим. Здесь у нас используется привязка NetNamedPipeBinding, то есть именованные каналы. И мы уже понимаем, что вот этого рода приложения, два приложения, будут, а точнее должны работать на одной машине. Они уже не могут общаться по сети. Почему? Потому что мы используем, так сказать, техники IPC. Несмотря на то, что у нас и контракт настоящий, и сервис как обычно, но мы видим, что у нас здесь используется техника IPC (Inter-process communication) – межпроцессные взаимодействия. И как мы видим, это определяется и привязкой, и транспортным префиксом адресом, мы используем именованные каналы. Этот пример желательно выполнять через Ctrl+F5, потому что при F5… давайте попробуем, выполнится ли при F5. Да, выполнился даже при F5. Хорошо. Давайте еще посмотрим один пример. И вернусь еще к этому примеру. Видите, NetNamedPipeBinding, она также настраивается более тонко. Указываем количество соединений, размер одного входящего сообщения, имя привязки, видите, да. Вот тоже достаточно тонко настраивается.
Переходим к следующему примеру. И если мы посмотрим теперь на привязку BasicHttpBinding, то смотрите, у нее намного скуднее свойства. То есть мы здесь уже не можем использовать много свойств, которые присущи другим привязкам, таким привязкам, которые содержат в себе больше элементов, потому что мы помним, что BasicHttpBinding состоит всего лишь из двух трубочек. Вот эта большая труба из двух. А те, которые мы рассматривали до этого, у них 3, и 4 и 5 вот этих элементов. Поэтому здесь у нас меньший набор свойств. И поэтому смотрите внимательно, если Вам придется как-то более тонко настраивать эти привязки.
И давайте посмотрим еще один пример. Это формирование кастомных, или пользовательских привязок. Вот Вы сказали «Все, ничего мне не помогает, не помогает мне настройка binding», представьте себе, что Вы уже умеете тонко настраивать эти привязки, говорите «Ничего мне не помогает, буду создавать свою привязку». Хорошо. И вот мы заходим в контракт. Смотрим, контракт простейший. И сервис простейший. Даже зайдем на клиент. Смотрите, а на клиенте и на сервере у нас уже формируются другого рода привязки. На 20 строке создаем сервис хост и 24 строке создаем экземпляр класса CustomBinding. И теперь смотрите, у нас имеется коллекция, как Вы видите, коллекция элементов типа BindingElement. Вот, давайте посмотрим на нее. Видите, коллекция BindingElementCollection. И мы эту коллекцию, в коллекцию начинаем добавлять экземпляры классов – BinaryMessageEncodingBindingElement. Вы говорите «Я хочу использовать бинарную сериализацию, что бы мои сообщения все шли в бинарном виде». Ни в текстовом, ни в каком другом, а в бинарном. Далее, хочу использовать TCP транспорт. И, говорите, больше ничего не хочу. Не хочу никаких TCP-шных защит, потому что стандартная TCP-ная привязка Вам выдает кучу защит. Вот мы можем зайти сейчас и вспомнить. Откроем open folder in file explorer и зайдем в пример, который нам показывал простейшие элементы привязки. Если мы использовали NetTcpBinding, смотрите, у них еще какие-то разные StreamSecurity, вот Вы BinaryMessageEncoding выбрали, какие-то TransactionFlowBindingElement контролит транзакции. Вы говорите «Та не надо мне никаких транзакций, не буду я там рассчитывать, что произойдут какие-то ролл беки, просто вот хочу TCP», ну, к примеру, мы сейчас не будем углубляться в правила использования тех или иных элементов, из которых состоит привязка. Говорите, что хочу себе простую. Ну, Вы и берете, грохаете все эти четыре элемента, грохаете и говорите «Вот хочу себе создать что-то похожее». BinaryMessageEncodingBindingElement, Вы его взяли, выбрали себе, и TcpTransportBindingElement выбрали. Говорите «Отключаю я себе security, ничего я не боюсь, и меня не волнует транзакция, все вот эти схемы отката транзакций, состояния, мне в данном случае не надо». И вот Вы взяли и сформировали свою вот такую привязку, вот такую урезанную NetTcpBinding привязку. Задаем, снова же, время ожидания закрытия соединения – ждать долго, MyBinding – имя привязки, время ожидания завершения операции открытия соединения, это значит клиент, сервис консьюмер, посылает запрос и ждет, пока откроется соединение. Если соединение, Вы говорите «Вот, хочу ждать 3 минуты», например. Или минуту. И время ожидания завершения операции приема. И, смотрите, мы на 42 строке используем стандартный сервис хост, видите, указываем сюда адрес net.tcp, работаем по net.tcp. Он смотрит, точно ли у Вас net.tcp элемент. Вы говорите «Да, да, да, вот я вставил сюда». Он говорит «Все, хорошо». Видите, потому что здесь же указан binding и система сравнивает, действительно ли у Вас транспортный префикс адреса совпадает с транспортным элементом привязки, потому что если не совпадают, то уже будет проблема, уже не произойдет соединение. Еще раз, A B C, азбука WCF, - Address, Binding, Contract. Ну и, по сути, все остальные элементы примера, вот мы видели сервис, контракт. И так же смотрите, мы должны на клиенте так же сконфигурировать custom binding. Но здесь уже нам не обязательно тонко настраивать, потому что эта синхронизация произойдет автоматически, когда клиент обратится к сервису. Давайте выполнимся и посмотрим, действительно ли у нас все работает. Здесь нам предлагается Windows Security Alert, да, Allow access, так как мы работаем с tcp. Вот, пожалуйста. Клиент послал запрос серверу, сервер ему ответил хэш-кодом своего сервиса. Хорошо.
А мы переходим с Вами дальше. И рассмотрим такие понятия как кодировки сообщений. И на этом слайде мы с Вами видим, что WCF поставляется с тремя кодировщиками – это текстовый, бинарный и MTOM (Message Transmission Optimization Mechanism). И давайте их рассмотрим более детально. То есть мы уже сталкивались с текстовым кодированием – это наиболее простой и распространённый кодировщик, помните привязку BasicHttpBinding, вот там как раз и используется текстовый кодировщик. Мы видели, когда смотрели на элементы этой привязки. Бинарный кодировщик – он является наиболее производительным, скажем так, и предназначен только для коммуникаций WCF to WCF, WCF и консьюмер, WCF и провайдер. Тогда есть смысл использовать бинарный. А вот кодировщик MTOM (Message Transmission Optimization Mechanism) создает сообщения, закодированные согласно спецификациям MTOM. Эта спецификация доступна в Интернете, я думаю, что Вы через поисковые системы можете найти и кратко ознакомится с ней. Так вот, сообщения MTOM, они могут быть отправлены в приложения не на базе WCF, то есть, допустим, с использованием каких-то других сервисов. И MTOM предназначен именно для эффективной передачи сообщений, которые содержат именно бинарные данные. Например, картинки, звуки, видео, а также для предоставления механизмов цифровой подписи. Давайте перейдем к следующему слайду. Ну, хотя, давайте вернемся и посмотрим на эти кодировщики физически. Давайте вернемся к нашему примеру. Смотрим, где у нас здесь кодировщик? Вы скажите «Вот 25 строка, используется бинарный кодировщик в нашем последнем примере, который мы рассмотрели». Если мы сделаем go to definition в BinaryMessageEncodingBindingElement, то мы заходим и видим, что в итоге он наследуется от MessageEncodingBindingElement. И мы понимаем, если у нас имеются 3 кодировщика, это значит, что где-то есть их тип, который наследуется вот от этого класса MessageEncodingBindingElement. И мы зайдем в Object Browser и смотрим – да, действительно, у нас имеется MessageEncodingBindongElement и вот его Derived Types – BinaryMessageEncodingBindingElement, бинарный кодировщик, только WCF работает, текстовый – TextMessageEncodingBindingElement, здесь он работает абсолютно со всеми. Снова же – Binary это для WCF, текстовый – со всеми. Ну, это идет кодировка в неоптимальном текстовом формате. И вот, посмотрим еще на один кодировщик, вот он – кодировщик MTOM, тоже он находится в пространстве имен System.ServiceModel.Channels, давайте мы его посмотрим еще раз. Вот у нас идет MtomMessageEncodingBindingElement и он тоже наследуется от базового класса MessageEncodingBindingElement. Вот три этих кодировщика, которые мы можем использовать, даже при формировании своих собственных привязок.
Также давайте рассмотрим вопросы, которые касаются выбора подходящей кодировки. То есть, конечно же, выбор кодировки – он связан с текущим и будущим назначением сообщений. Бинарная кодировка, как мы знаем, является наиболее эффективной для отправки сообщений другим системам, так сказать, WCF системам. MTOM кодировка – она недолжна использоваться сообщениями не содержащими бинарные данные, потому что производительность будет хуже. Если мы текстовые данные начнем кодировать в MTOM бинарную кодировку для других платформ сервисов, то там будет происходить лишняя сериализация, упаковка ее и будет, так сказать, хуже производительность. И вот давайте посмотрим, вот прочитаем, что рекомендует нам эта таблица. Тип сообщения – текстовая нагрузка, взаимодействие с другими системами только на базе WCF. Текстовая нагрузка – гоняем большие тексты. Обратите внимание, 1,2,3 – это приоритеты использования этой кодировки. Большие объемы текста, гоняем произведение «Война и Мир» между сервисами. Бинарный (1), текстовый (2), МТОМ(3). Вот у нас идут приоритеты. Лучше всего использовать Microsoft-овскую бинарную кодировку. Почему? Потому что там идет естественная сериализация. Помните, мы смотрели, как передаются сообщения, они сериализуются, десериализуются, и вот эта бинарная сериализация – она самая быстрая. Если Вы помните сериализацию, то там есть такой класс Binary Formatter – самый быстрый. Далее – текстовая нагрузка, взаимодействие с современными системами не на базе WCF, с какими-то другими сервисами от других производителей. Тогда «Война и Мир» лучше гонять в текстовом формате, потому что МТОМ будет немножко неэффективный. Текстовая нагрузка, взаимодействие со старыми системами не на базе WCF – в текстовом, другие даже не доступны. Большая бинарная нагрузка, гоняем видео, изображения, взаимодействие с другими системами только на базе WCF. Снова же, рекомендуется использовать бинарную. Большая бинарная нагрузка, взаимодействие с современными системами не на базе WCF. Конечно же МТОМ, на втором текстовый. Ну, представьте, Вам перекодировать видеопоток в текст, а потом его декодировать обратно. Странно, да, звучит, видео в текст, очень много чего может и поменяться, зависит все от инкодинга, который будет применяться. И большая бинарная нагрузка, гоним видео, взаимодействие со старыми системами не на базе WCF. Приходится работать только с текстом, это конечно ужасно. Маленькая бинарная нагрузка, гоняем, например, аватарки, какие-то маленькие файлы. Microsoft говорит – бинарная самая быстрая. Маленькая бинарная, взаимодействие с современными системами не на базе WCF – текстовый. И маленькая бинарная нагрузка, со старыми системами – используем, снова же, текстовый подход.
Переходим к следующему слайду и посмотрим еще вот такой момент. Правила – выбор кодировки не влияет на структуру приложения. О чем это говорит? О том, что у нас может быть один сервис, у которого может быть несколько конечных точек. Обратите внимание, здесь у нас имеются первая конечная точка BasicHttpBinding. Вы говорите «Да, здесь будет использоваться текстовая кодировка». NetTcpBinding. Вы скажите «Мы уже видели, что здесь будет идти бинарная кодировка, если это WCF-ный сервис». Но мы в курсе по WCF рассматриваем WCF сервисы, а сервисы сторонних производителей и интеграции, с ними выходит за пределы, по крайней мере, базового курса. Еще раз, мы можем иметь сервис, который будет взаимодействовать с различными кодировками. И уже Ваша воля выбирать себе нужную конечную точку, через которую Вы и будите общаться с экземпляром требуемого Вам сервиса.
И переходим к последнему слайду. Выводы, что мы изучили на сегодняшнем уроке.
1)Мы изучили различные вариации активизации экземпляров сервисов. То есть мы видим, что WCF поддерживает 3 типа активизации экземпляров: службы уровня вызова (PerCall), службы уровня сеанса (PerSession) и синглетные службы.
2)Мы с Вами рассмотрели, что такое стек каналов, что такое канал. Мы видим, что канал – это многоуровневый (много элементов, серых трубочек) коммуникационный стек, составленный из одного или нескольких (снова же, вот этих трубочек) каналов, которые обрабатывают сообщение.
3)И привязки – это заранее сконфигурированные стеки каналов для взаимосвязей определенного вида. В комплекте с WCF поставляются девять стандартных привязок. Есть еще несколько нестандартных привязок, которые мы не рассматривали. Но мы понимаем, что мы можем составлять свои собственные кастомные привязки и видим, как это просто, как в каком-то детском конструкторе. Взяли и просто собрали. Представьте себе, что канал, это Ваш, так сказать, стакан для карандашей и там стоят разные карандаши. И Вы просто взяли два карандаша и составили. То есть старайтесь мыслить вот так.
На этом наш урок закончен. Спасибо за внимание! С Вами был Александр Шевчук. До новых встреч на ITVDN!