Оценка 595
[+0/-1]
Оценить ![]() ![]() ![]() ![]() ![]() ![]()
|
| Что такое Indigo Первые шаги Сервис с одной реализацией и несколькими контрактами Архитектура Indigo | ![]() |
Примеры к статье: Пример1, Пример 2.
Indigo – это новая коммуникационная подсистема Windows, предназначенная для создания распределенных приложений. Основная задача Indigo – обеспечить взаимодействие частей распределенного приложения. Помимо этого она обеспечивает безопасность, транзакционность и надежность коммуникаций.
Серверная часть приложения, использующего Indigo, называется сервисом. Сервис, прежде всего, содержит функциональность, которую необходимо реализовать на сервере (реализацию). Кроме того, он может содержать метаданные, необходимые для его публикации, и контракта – абстрактного описания методов, параметров и возвращаемых значений точки доступа (endpoint). Взаимодействие с клиентом осуществляется посредством сообщений.
Простейший способ создания как реализации, так и контракта – это разметка обычного класса специальными атрибутами, ServiceContractAttribute и OperationContractAttribute. Первым нужно пометить класс, который является сервисом, вторым – методы, которые будут доступны публично.
| ПРИМЕЧАНИЕ На первый взгляд это очень похоже на Web-сервисы. Однако есть различия. Прежде всего, в Indigo нет необходимости наследовать класс от специального класса. Необходимость такового признана архитектурной ошибкой, и вместо наследования предложен атрибут. Это повышает гибкость решений при построении распределенных приложений. Еще одно отличие – поддержка сессий теперь задается не для методов, а для сервиса целиком. Это больше соответствует принципам построения сервисов. |
Ниже приведена таблица, описывающая возможные способы хостинга сервисов, и те их особенности, на которые указывает Microsoft.
| Способ хостинга | Сценарий применения | Ключевые особенности | Ограничения |
|---|---|---|---|
| IIS 5.1, IIS 6.0 | Работа сервисов в составе ASP.NET приложений.Работа сервисов в интернете.Работа сервиса на машине разработчика с установленным IIS. | Среда сервиса интегрирована со средой ASP.NET, следовательно, доступны все преимущества этой среды – пересоздание процессов при утечках памяти и критических сбоях, остановка сервиса при отсутствии обращений, мониторинг состояния процесса и активация по запросу извне. | Поддерживается только протокол HTTP. |
| IIS 7.0 | Работа сервисов в составе ASP.NET-приложений.Работа сервисов в интернете.Работа сервиса на машине разработчика с установленным IIS. | Построен поверх WAS, так что обладает всеми его возможностями. Кроме того, доступно взаимодействие с ASP.NET приложениями. | В настоящий момент поддерживается только в Longhorn. |
| Сервис Windows | Работа сервисов в качестве сервиса Windows, таким образом, что они могут быть сконфигурированы для запуска при старте ОС. | Сервис может быть запущен автоматически. Возможна работа со всеми поддерживаемыми Indigo протоколами в ОС Windows XP, Windows Server 2003, Windows “Longhorn”. | Возможности мониторинга и управления процессами ограничены возможностями используемой ОС. |
| Windows Activation Services (WAS) | Работа сервиса в среде, где нет желания устанавливать IIS. | Обеспечивает все возможности, предоставляемые IIS без необходимости устанавливать Web-сервер.Поддерживает протоколы TCP, HTTP, IPC и MSMQ.Предоставляет возможность автоматической активации сервисов, специальным сервисом активации, наподобие того, как работает активация для приложений COM. | В настоящий момент поддерживается только в Longhorn. |
| Отдельное приложение | Работа сервиса в среде, где нет желания устанавливать IIS. | Запускается вручную. Возможна работа со всеми поддерживаемыми Indigo протоколами в ОС Windows XP, Windows Server 2003, Windows “Longhorn”. |
| ПРИМЕЧАНИЕ Однако все эти способы в итоге сводятся к использованию ServiceHost<T>, о котором мы поговорим ниже. |
Для начала попробуем создать простейшее приложение с использованием новой технологии. Пусть это будет сервис, возвращающий текущее время на сервере.
| ПРИМЕЧАНИЕ Все описания и исходные коды соответствуют Microsoft WinFX SDK – "Avalon" and "Indigo" Community Technology Preview Edition (Март 2005). Версия сборок Indigo – 2.0.5110.20. |
Напишем класс, реализующий требуемую функциональность, и разметим его:
using System;
using System.ServiceModel;
namespace Service
{
/// <summary>/// Сервис, возвращающий время сервера./// </summary>
[ServiceContract]
publicclass TimeService
{
/// <summary>/// Возвращает время сервера./// </summary>
[OperationContract]
public DateTime GetServerTime()
{
Console.WriteLine("GetServerTime() called");
return DateTime.Now;
}
}
}
|
После того как мы создали реализацию, необходимо опубликовать сервис, чтобы он был доступен извне. Существует несколько способов сделать это, но в данном случае мы воспользуемся самым наглядным – хостингом сервиса в собственном приложении.
Для формирования среды хостинга служит generic-класс ServiceHost<T>. Ниже приведен пример программы, осуществляющей хостинг сервиса.
using System;
using System.ServiceModel;
namespace Service
{
class Program
{
staticvoid Main(string[] args)
{
// Создаем базовый адрес.
Uri baseHttpAddress = new Uri("http://localhost:8080");
// Формируем среду исполнения сервисаusing (ServiceHost<TimeService> host = new ServiceHost<TimeService>(baseHttpAddress))
{
// Создаем совместимый с веб-сервисом HTTP binding.
BasicProfileBinding binding = new BasicProfileBinding();
// Добавляем точку доступа к сервису
host.AddEndpoint(typeof(TimeService), binding, "/TimeService");
// Запускаем прослушивание
host.Open();
// Выводим подсказку и останавливаем основной поток
Console.WriteLine("Service is running. Press ENTER to exit...");
Console.ReadLine();
}
}
}
}
|
В первой строке мы создаем URI сервиса, по которому он будет доступен. Далее создаем экземпляр хоста. Следующим шагом идет создание т.н. связывания (binding), способа взаимодействия сервиса с клиентом. Связывание отвечает за протокол взаимодействия, и в какой-то мере является аналогом каналов (channels) в Remoting. В примере используется самое простое связывание, совместимое с Web-сервисами и обеспечивающее взаимодействие по протоколу HTTP с использованием SOAP.
После создания связывания создаем точку доступа к сервису (endpoint). Этой операции не было в Web-сервисах, поскольку такая точка у Web-сервиса могла быть только одна. В случае Indigo мы можем задать несколько точек, при этом выставив в разные точки разные интерфейсы (контракты) сервиса, и назначить им различные права доступа, URL, протоколы и т.п.
Наконец, вызовом метода Open() переводим хост-объект в режим ожидания вызова.
Теперь, если набрать в браузере адрес http://localhost:8080, мы должны получить такую страничку:

По завершению работы для освобождения ресурсов необходимо вызвать у хост-объекта метод Close() или использовать конструкцию using для автоматического вызова метода Dispose.
Помимо прямого задания параметров хост-объекта, связывания и точек входа, можно задавать их декларативно, примерно так же, как раньше это делалось в Remoting. Перепишем наш пример для демонстрации этого. Прежде всего в файле конфигурации приложения заполняем секцию конфигурации Indigo.
<?xmlversion="1.0"encoding="utf-8" ?> <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <appSettings> <add key="baseAddress" value="http://localhost:8080" /> </appSettings> <system.serviceModel> <services> <service serviceType="Service.TimeService"> <endpoint bindingSectionName="basicProfileBinding" contractType="Service.TimeService"/> </service> </services> </system.serviceModel> </configuration> |
Теперь код формирования точек доступа можно убрать. Итоговый текст программы будет выглядеть следующим образом:
using System;
using System.Configuration;
using System.ServiceModel;
namespace Service
{
class Program
{
staticvoid Main(string[] args)
{
// Формируем базовый адрес из настроек приложения.
Uri baseHttpAddress =
new Uri(ConfigurationManager.AppSettings["baseAddress"]);
// Создаем сервис и запускаем прослушивание
using (ServiceHost<TimeService> host =
new ServiceHost<TimeService>(baseHttpAddress))
{
host.Open();
Console.WriteLine("Service is running. Press ENTER to exit...");
Console.ReadLine();
}
}
}
}
|
Обратите внимание на то, что, в отличие от Remoting, нет необходимости явно вызывать аналог RemotingServices.Configure. Конфигурирование производится автоматически, на основании типа сервиса.
Теперь нам необходимо написать клиентский код, использующий сервис. Поскольку использованное нами связывание совместимо с Web-сервисами, то можно просто использовать обычную Web-ссылку (web reference), однако мы используем родные средства Indigo.
Для начала, так же, как и в случае с Web-сервисами, необходимо создать прокси-классы, представляющие сервис на клиенте. Для создания таких классов существует утилита svcutil. Ее необходимо запустить, указав в качестве аргумента URL сервиса. Помимо URL сервиса можно указать массу дополнительных параметров, как совпадающих с таковыми у wsdl, так и новыми (например, можно сделать генерируемые классы сериализуемыми).
В результате работы утилиты получаем такой исходный код:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50110.28
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[System.ServiceModel.ServiceContractAttribute()]
publicinterface TimeService
{
[System.ServiceModel.OperationContractAttribute(
Action = "http://tempuri.org/TimeService/GetServerTime",
ReplyAction = "http://tempuri.org/TimeService/GetServerTimeResponse")
]
[return: System.ServiceModel.MessageBodyAttribute(
Name = "GetServerTimeResult", Namespace = "http://tempuri.org/")
]
System.DateTime GetServerTime();
}
publicinterface TimeServiceChannel
: TimeService, System.ServiceModel.IProxyChannel
{
}
public partial class TimeServiceProxy
: System.ServiceModel.ProxyBase<TimeService>, TimeService
{
public TimeServiceProxy()
{
}
public TimeServiceProxy(string configurationName) :
base(configurationName)
{
}
public TimeServiceProxy(System.ServiceModel.Binding binding) :
base(binding)
{
}
public TimeServiceProxy(System.ServiceModel.EndpointAddress address,
System.ServiceModel.Binding binding)
: base(address, binding)
{
}
public System.DateTime GetServerTime()
{
returnbase.InnerProxy.GetServerTime();
}
}
|
Сравним результат с тем, что генерирует утилита wsdl для того же сервиса:
Очевидно, прокси-класс Indigo намного компактнее. Прежде всего потому что не произошло формирования прокси-класса для аргументов. Вместо фиктивного dateTime в случае WSDL в Indigo мы видим полноценный System.DateTime. Базовый класс теперь представляет собой generic-класс, поэтому нет никаких приведений при обращении к его методам и свойствам, а сам прокси-класс обзавелся типизированным интерфейсом (это позволит использовать несколько разных прокси-классов с одинаковыми контрактами). Нет массы асинхронных методов – функциональность асинхронных вызовов вынесена наружу. Наконец, вместо конструктора без параметров и жесткой привязки к конкретному URL мы имеем несколько перегруженных конструкторов, в параметрах которых можно передать требуемое связывание и точку доступа.
Можно еще заметить интерфейс TimeServiceChannel. Он предназначен для управления запросами и сессиями на клиенте.
Осталось только воспользоваться полученным кодом. Работать с сервисами Indigo так же просто, как и с веб-сервисами. Единственное отличие, видное сразу – экземпляры прокси-классов необходимо явно уничтожать.
Набросаем простую форму, содержащую кнопку вызова серверного метода и TextBox для вывода результатов.

Теперь напишем обработчик нажатия кнопки.
private
void _getServerTimeButton_Click(object sender, EventArgs e)
{
using (TimeServiceProxy proxy = new TimeServiceProxy(
new EndpointAddress("http://localhost:8080/"),
new BasicProfileBinding()))
_serverTimeBox.Text = proxy.GetServerTime().ToString();
}
|
Код предельно прост – создаем экземляр прокси-класса, передав на вход адрес сервиса и связывание, после этого вызываем метод.
Запускаем и убеждаемся, что все работает. Простейшее Indigo-приложение создано.
Конфигурацию клиентского прокси-объекта также можно сделать декларативно. В описываемом примере достаточно добавить конфигурационный файл со следующим содержимым:
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.serviceModel>
<client>
<endpoint
address="http://localhost:8080"
bindingSectionName="basicProfileBinding"
contractType="TimeService" />
</client>
</system.serviceModel>
</configuration>
|
В этом случае передавать параметры в конструктор прокси-класса не нужно.
Теперь усложним задачу. Создадим сервис, у которого одной реализации соответствуют несколько контрактов. Возьмем за основу сервис из предыдущего раздела и добавим дополнительный контракт для управления самим сервисом.
Для начала отделим контракт от реализации. Опишем его в виде отдельного интерфейса.
using System;
using System.ServiceModel;
namespace Service
{
[ServiceContract]
interface ITimeService
{
[OperationContract]
DateTime GetServerTime();
}
}
|
Теперь опишем интерфейс управления.
using System;
using System.ServiceModel;
namespace Service
{
[ServiceContract]
publicinterface ITimeServiceController
{
[OperationContract]
void StopServer();
}
}
|
Наконец, реализация.
using System;
using System.ServiceModel;
namespace Service
{
/// <summary>/// Сервис, возвращающий время сервера./// </summary>internalclass TimeService : ITimeService, ITimeServiceController
{
/// <summary>/// Возвращает время сервера./// </summary>public DateTime GetServerTime()
{
Console.WriteLine("GetServerTime() called");
return DateTime.Now;
}
/// <summary>/// Останавливает сервер./// </summary>publicvoid StopServer()
{
Console.WriteLine("StopServer() called");
Program.ServerRunEvent.Set();
}
}
}
|
Обратите внимание на то, что разметка перекочевала в интерфейсы, а реализация получила модификатор доступа internal. Такая механика позволяет спрятать реализацию внутри сборки – возможность, раньше доступная только Remoting и недоступная Web-сервисам.
Управление работой сервера осуществляется объектом синхронизации "событие".
Ниже приведен декларативный вариант описания точек доступа.
<system.serviceModel>
<services>
<service serviceType="Service.TimeService">
<endpoint address=""
bindingSectionName="basicProfileBinding"
contractType="Service.ITimeService"/>
<endpoint address="controller"
bindingSectionName="basicProfileBinding"
contractType="Service.ITimeServiceController"/>
</service>
</services>
<system.serviceModel>
|
Точек теперь две и у них разные типы контрактов.
В запускающий код сервера добавлено управление событием.
using System;
using System.Configuration;
using System.ServiceModel;
using System.Threading;
namespace Service
{
class Program
{
internalstatic ManualResetEvent ServerRunEvent;
staticvoid Main(string[] args)
{
Uri baseHttpAddress = new Uri(
ConfigurationManager.AppSettings["baseAddress"]);
using (ServiceHost<TimeService> host =
new ServiceHost<TimeService>(baseHttpAddress))
{
ServerRunEvent = new ManualResetEvent(false);
host.Open();
Console.WriteLine("Service is running ...");
ServerRunEvent.WaitOne();
// Подождем отработки последнего вызова
Thread.Sleep(300);
}
}
}
}
|
Thread.Sleep в конце нужен для того, чтобы сервис успел отработать все текущие запросы.
На клиенте создаем новые прокси и вносим соответствующие правки в конфигурацию.
<system.serviceModel>
<client>
<endpoint
address="http://localhost:8080"
bindingSectionName="basicProfileBinding"
contractType="ITimeService" />
<endpoint
address="http://localhost:8080/controller"
bindingSectionName="basicProfileBinding"
contractType="ITimeServiceController" />
</client>
</system.serviceModel>
|
Запускаем и убеждаемся, что все работает по-прежнему. Теперь в обработчик события нажатия кнопки Exit добавляем завершение работы сервера.
private
void _exitButton_Click(object sender, EventArgs e)
{
using (TimeServiceControllerProxy proxy = new TimeServiceControllerProxy())
proxy.StopServer();
Close();
}
|
Запускаем и убеждаемся, что при нажатии кнопки Exit сервер завершает работу.
После того как мы попробовали создать простейшее распределенное приложение, попытаемся немного подробнее разобраться с тем, что же все таки представляет собой новая система коммуникаций.
Прежде всего, следует отметить, что Indigo не создавалась на пустом месте. В ее основе лежат более старые технологии – COM+, MSMQ, Remoting и Web-сервисы ASP.NET. Была предпринята попытка взять от этих технологий самое лучшее, и на полученной основе создать единый фреймворк для создания распределенных приложений.
Кроме того, Indigo была спроектирована в расчете на взаимодействие не только с новыми приложениями, но и со старыми. Для этого в нее добавлена поддержка большого количества транспортных протоколов (HTTP, TCP, UDP, IPC); стандартные механизмы аутентификации и шифрования; различные топологии взаимодействия (клиент-сервер, P2P, издатель-подписчик): поддержка транзакций.
В качестве основы построения распределенных приложений Indigo предлагает сервис-ориентированную архитектуру (SOA). Остановимся на ней поподробнее.
Ориентация на сервисы – это подход к построению распределенных приложений, служащий дополнением к ООП и компонентному программированию. Если в ООП строительным блоком приложения служит класс, в SOA таковым является автономный сервис. Сервис – это программа, которая способна обмениваться с внешним миром сообщениями. Набор развернутых сервисов образует распределенную систему. Сервисы и клиенты должны быть спроектированы таким образом, чтобы добавление новых сервисов не сказалось на функционировании остальной части системы.
Microsoft выделяет 4 основных принципа построения сервис-ориентированных систем:
1. Границы отражены явно. Современные распределенные системы географически могут быть расположены чрезвычайно широко. Скорость и надежность каналов коммуникаций между частями может сильно различаться. Поэтому сервис-ориентированное приложение должно явно выделять процесс преодоления границ между частями системы, вместо того чтобы делать это неявно при вызове метода. Это позволяет избежать проблем, связанных с нестабильностью и низкой скоростью глобальных каналов коммуникаций.
Идеология явного выделения границ коммуникации распространяется не только на различные сервисы различных поставщиков, но и на разработку нескольких сервисов внутри одной компании, поскольку в ходе дальнейшего развития системы может понадобиться использовать эти сервисы удаленно. Упрощение и универсализация сервисов в сервис-ориентированной архитектуре – это жизненно важный момент для создания надежных и легко изменяемых SOA-приложений.
2. Сервисы автономны. Поскольку части распределенной системы разнесены очень широко, невозможно гарантировать, что все ее части будут работоспособны, иметь одинаковые версии и т.п. Поэтому при проектировании таких приложений необходимо рассматривать как штатную ситуацию отсутствие некоторых сервисов, сбои в каналах связи или неполные совпадения контрактов. Кроме того, поскольку внутрисистемные коммуникации могут проходить по открытым каналам связи, необходимы аутентификация, шифрование и проверка целостности межсервисных сообщений.
3. Сервисы описываются схемой и контрактом, а не классами. Необходимость сохранения работоспособности системы при смене версии сервиса выдвигает дополнительные требования к гибкости описания контракта сервиса. Описание, сходное с ОО-интерфейсами, для сервисов не годится, поскольку не обеспечивает должного уровня совместимости. Поэтому в SOA следует использовать гибкий формат сообщений, снабженный машинно-проверяемыми схемами, а также возможностью передавать дополнительную информацию, не нарушая структуры основной информации, чтобы обеспечить работоспособность уже использующегося кода.
4. Совместимость сервисов определена и основана на политике совместимости. В классическом ООП на совместимость частей кода влияет как структура публичных интерфейсов кода, так и семантика действий, выполняемых этим кодом. В SOA эти два аспекта совместимости объединены в один. Структурная совместимость обеспечивается машинным контролем формата сообщений на соответствие схеме, семантическая – политикой совместимости.
Политика совместимости представляет собой набор машинно-читаемых выражений. Выражения описывают условия и гарантии (называемые утверждениями, assertion), которые должны быть обеспечены для нормального функционирования сервиса. Утверждения политики идентифицируются стабильным глобально уникальным идентификатором, вне зависимости от того, к какому сервису они применяются. Утверждения должно быть достаточно формальными, чтобы обеспечить простую механику их проверки.
Модель сервисов предназначена для легкого использования Indigo в CLR-коде. Она представляет собой набор атрибутов, предназначенных для разметки контрактов сервиса. Поддерживаются два типа контракта: контракт сервиса и контракт данных.
Контракт сервиса описывает способ, которым можно взаимодействовать с сервисом. Для создания контракта сервиса достаточно применить атрибут ServiceContractAttribute к публичному типу, описывающему сервис. Это может быть как реализация сервиса (как в первом примере), так и отдельный интерфейс (как во втором).
Контракт данных описывает структуру сообщения. Этот вид контракта аналогичен XML-схеме, и описывает способ преобразования CLR-типов в сообщения. Для создания контракта данных достаточно пометить тип атрибутом DataContractAttribute, а его поля или свойства, являющиеся частью контракта, атрибутом DataMemberAttribute.
Основной задачей модели сервисов является обеспечение связи между контрактом и пользовательским кодом. Эта задача решается атрибутом OperationContractAttribute. Методы, помеченные этим атрибутом, являются обработчиками определенного типа сообщений. В приведенных выше примерах мы использовали этот атрибут для пометки методов, которые потом вызывали удаленно. Обработчики могут быть как однонаправленными, так и работающими в режиме запрос-ответ, что определяется значением свойства IsOneWay атрибута. В процессе работы сообщение преобразовывается в типизированный набор параметров, а возвращаемое значение преобразовывается в ответное сообщение.
Еще одной задачей, которую выполняет модель сервисов, является поддержание контекста работы. В понятие контекста входят сессии, поддержка контекста вызова, поддержка транзакций.
Наконец, модель сервисов Indigo обеспечивает декларативный механизм связывания сервисов и контроля безопасности взаимодействия. В примерах использовалось связывание, обеспечивающее взаимодействие в стиле Web-сервисов.
Indigo содержит несколько сервисов, предназначенных для упрощения реализации отдельных аспектов распределенных систем. Например, для обеспечения сложных схем авторизации с установлением доверительных отношений между различными сервисами и обменом идентификационной информацией (identity federation). В будущих версиях Windows будет использоваться подобная схема, основанная на WS-Federation (протокол обмена идентификационной информацией между Web-сервисами).
Indigo также обеспечивает значительную поддержку программирования с использованием транзакций. Для поддержки транзакций может использоваться соответствующий протокол Web-сервисов (WS-AtomicTransactions), либо новый фреймворк, интегрированный с Windows – System.Transaction. Этот фреймворк обеспечивает взаимодействие с существующими системами транзакций (DTC, WS-AtomicTransactions), предоставляет облегченный менеджер транзакций, хранящий состояние транзакции в памяти и не поддерживающий постоянные хранилища, позволяет легко создавать собственные менеджеры ресурсов в управляемом коде. В ОС Windows Longhorn, кроме того, добавится поддержка нового менеджера транзакций, интегрированного в ядро (KTM, KernelTransaction Manager) и транзакционного режима работы NTFS (TxNTFS).
Для создания ориентированных на сообщения приложений существует также набор реализаций основных абстракций, используемых для этого. Indigo обеспечивает набор простых, но расширяемых очередей, событий и механизмов их роутинга.
Оценка 595
[+0/-1]
Оценить ![]() ![]() ![]() ![]() ![]() ![]()
|