Книга: Дефрагментация мозга. Софтостроение изнутри
От CORBA к SOA
От CORBA к SOA
Признаю, что название главы логически не совсем корректно, поскольку CORBA[73] – одна из технологий создания сервис-ориентированной архитектуры, СОА[74], оно отражает хронологию развития событий. Поскольку маркетинговый шум вокруг СОА стих, жужжащие словечки вроде «loose coupling» подзабылись, а в последние годы стало модным даже говорить о том, что «СОА выдохлась», можно вернуться к сюжету в спокойной обстановке на уровне собственно технологии. Мне слово «СОА» напоминает другое из 1980-х годов, СОИ – Стратегическая Оборонная Инициатива, оказавшаяся пропагандистским пузырём игры в «звёздные войны» администрации американского президента Рейгана. Просто ассоциации, ничего личного.
Предшественником современной СОА на базе веб-служб, с полным правом можно считать CORBA. Это сейчас задним числом обобщают подходы, представляя СОА как набор слабосвязанных сетевых служб, реализовать которые можно по-разному. А тогда, в середине 1990-х, вокруг CORBA стоял достаточно сильный шум продавцов волшебных палочек, но никаких упоминаний СОА ещё не было. Только начинал своё бурное развитие интерактивный веб динамического контента, приём заказов через интернет-магазин считался «крутой фишкой» для компании, а где-то в недрах Microsoft из XML-RPC[75] тихо прорастал SOAP[76].
CORBA имела очевидные достоинства, прежде всего это интероперабельность[77] и отраслевая стандартизация не только протоколов (шины), но и самих служб и общих механизмов – facilities. Например, служб имён, транзакций, безопасности, хранения объектов, конкурентного доступа, времени и т. п. Однажды реализованный программистами сервис мог использоваться многими системами без какой-либо дополнительной адаптации и ограничений с их стороны. Следует отметить, что фактически проигнорированная отраслью CORBA 1.0 не была интероперабельной и предназначалась только для языка C.
Почему же и CORBA 2, продержавшись несколько лет в основном потоке технологий, была оттеснена на обочину? Причин много, весьма подробный анализ описан в статье одного из разработчиков стандарта [14]. Мне же, как участнику разработки реальной системы на основе технологии, хотелось бы выделить следующие:
• Стандарт поддерживался многими корпорациями, поэтому развитие требовало длительного согласования интересов всех участников. Некоторые из них продвигали свои альтернативы, например Sun Java RMI и Jini, Microsoft DCOM.
• Несмотря на реальную поддержку интероперабельности, множества языков и сред программирования, основным средством разработки оставался C++. Но код на C++, манипулирующий инфраструктурой CORBA и службами, является излишне сложным по сравнению с той же Java или даже Delphi. Практиковавшие подтвердят, остальным будет достаточно взглянуть на примеры в Сети. А Java-программисты не спешили использовать CORBA из-за упомянутых альтернатив.
• Запоздалая (1999 год), объёмная и весьма сложная спецификация компонентной модели. Java-сообщество к тому времени обладало альтернативой в виде EJB[78] с открытыми и коммерческими реализациями.
Кто знает, откажись тогда корпорация Sun от роли единственно правильного хранителя своей технологии, сосредоточься она на интеграции с CORBA и реализации спецификаций, возможно, её история не закончилась бы через 10 лет поглощением со стороны Oracle. Но Sun тогда, на пике бума доткомов[79], предпочла строить свой собственный мир, планируя накрыть им всю отрасль. Насколько простой оказалась придуманная в корпорации реальность, можно судить по фрагменту из статьи 2002 года «Страдает ли Java от сложности?» [16].
Новоиспечённый инженер после пяти лет обучения, имеющий базовые знания Java, XML и UML, после своего прихода на предприятие и перед началом работы был привлечён в понедельник к чтению публичного отчёта по J2EE v1.4 на 228 страницах.
На шести первых страницах этого отчёта значились ссылки на EJB, JSP, JMS, JMX, JCA, JAAS, JAXP, JDBC, JNDI. Эта новая версия J2EE и веб-служб предполагала знание концепций SOAP, SAAJ, JAX-RPC и JAXR. Каждый из этих акронимов имеет соответствующую специ фикацию.
Спецификация EJB 2.1 – это документ PDF на 640 страницах, который будет прочтён во вторник. Среда будет посвящена чтению документации по сервлетам Servlet 2.4 на 307 страницах. В четверг он штурмует документ по JSP 2.0 на 374 страницах. И так далее. .
После месяца интенсивных чтений наш инженер наконец готов начать продуктивную работу. .
Я добавил бы, что указанные в статье оценки как временных рамок, так и продуктивности работы излишне оптимистичны. Но вернёмся к CORBA.
Из небольшого списка известных в тот период открытых реализаций (Orba-cus, MICO, Robin) нами в продукте использовался omniORB. В комплекте к третьей версии выпуска 2000 года шла единственная реализованная служба именования (COS naming services) и библиотека синхронизации потоков – минимум, чтобы просто запустить разработку «с нуля». Впрочем, небольшой команде стартапа этого вполне хватило.
В начале 2000-х в ИТ-отрасли разразился кризис лопнувшего пузыря интернет-компаний, продвигать сложные и дорогие инфраструктуры, а коммерческие реализации CORBA стоили порядка тысячи долларов за рабочее место, стало очень трудно. Постепенно из кустов начали выкатывать на сцену рояль веб-сервисов, который из-за проблем с CORBA должен был стать новой платформой интеграции служб и приложений в гетерогенной среде. Концепции быстро придумали многообещающее название СОА. Надо сказать, что рояль не стоял без дела и в кустах: разработка веб-служб и развитие XML велись в конце 1990-х и активно продолжились в 2000-х годах. Поскольку это была технология, хотя и весьма базового уровня, но относительно простая, дешёвая и открытая не только на уровне спецификаций, но и в виде множества реализаций.
Говоря о базовом уровне технологии, я имею в виду не столько отсутствие сравнимой с CORBA функциональности и номенклатуры служб и средств[80], сколько необходимость программистам самим надстраивать над этим базисом многое из того, что было даже в самом минимальном варианте CORBA.
Веб, точнее, его основа, HTTP – среда без состояния и пользовательских сессий. В общедоступном Интернете такое решение было вызвано соображениями нагрузки, поскольку максимальное число запросов к серверу теоретически равно количеству всех устройств в сети. В корпоративной же системе нагрузку на службу можно (и нужно) рассчитать гораздо точнее. В итоге программистам, не связанным с веб-разработкой для Интернета, приходится восполнять недостаток средств протокола надстройками поверх него костылей, например, постоянно гоняя контекст и состояние в сообщениях или симулируя сессии по тайм-ауту.
В CORBA сессии поддерживались средой без дополнительных усилий. Если в частных случаях возникали вопросы нагрузки на поддержку соединений при большом количестве клиентов, они решались так же просто, как и в среде СУБД: приложение самостоятельно отсоединялось от сервера, выполнив пакет необходимых запросов. При желании нетрудно было также организовать и принудительное отсоединение по истечении заданного периода пассивности. Но для корпоративной службы, напомню, речь идёт обычно о десятках и сотнях активных сессий, поддержка которых в большинстве случаев укладывается в ресурсы серверов.
Неприятным следствием отсутствия сессий стала невозможность поддержки транзакций при работе с веб-службами. Для решения проблемы в рамках ООП необходим шаблон «Единица работы» (unit of work), суть которого в передаче веб-службе сразу всего упорядоченного множества объектов, подлежащих сохранению в управляемой сервером транзакции.
В общем случае шаблон является аналогом пакетной обработки транзакций, необходимой для сокращения времени жизни единичной транзакции. Например, в репликации данных между СУБД сиквел-операции передаются пачками. Но если раньше такой подход был разновидностью оптимизации и средством избавления от толстых транзакций, то теперь его необходимо было использовать всегда, вместо любой транзакции вообще.
Давайте сравним близкий к реальному псевдокод на сторонах клиента в рамках CORBA с псевдокодом в среде веб-служб. При поддержке сессии все достаточно прозрачно и не нуждается в комментариях.
Псевдокод транзакции в среде CORBA
CosTransactions.Current current = CosTransactions.CurrentHelper.Narrow(
orb.ResolveInitialReferences("TransactionCurrent"));
current.Begin();
try
{
store1.Remove(product, quantity);
store2.Append(product, quantity);
current.Commit();
}
catch (Exception e)
{
current.Rollback();
ShowError("Ошибка выполнения операции: " + e.toString());
}
В среде веб-служб в программе-клиенте приходится надстраивать абстракции DTO[81]. А в серверном приложении, где используются соединения, например, с СУБД или монитором транзакций, необходимо фактически дублировать предыдущий код с раскруткой объекта – единицы работы (unit of work) в реальную транзакцию.
Псевдокод транзакции в среде веб-служб
StoreServiceClient storeServiceClient = new StoreServiceClient(url);
StoreOperationDTO operation1 = storeServiceClient.CreateOperation(store1.Id);
operation1.Type = StoreOperations.Remove;
operation1.ProductId = product.Id;
operation1.Quantity = quantity;
StoreOperationDTO operation2 = storeServiceClient.CreateOperation(store2.Id);
operation2.Type = StoreOperations.Append;
operation2.ProductId = product.Id;
operation2.Quantity = quantity;
UnitOfWork uow = new UnitOfWork();
uof.RegisterDirty(operation1);
uof.RegisterDirty(operation2);
try
{
storeServiceClient.ProcessOperations(uow);
}
catch (Exception e)
{
ShowError("Ошибка выполнения операции: " + e.toString());
}
Вторым «упрощением» стал переход от понятных прикладному программисту деклараций интерфейсов объектов и служб на языке IDL[82] к WSDL[83] – описаниям, ориентированным, прежде всего, на обработку компьютером. Сравним декларации складской службы, возвращающей по запросу текущее количество товарных позиций.
Декларация службы в CORBA IDL
module StockServices
{
typedef float CurrentQuantity;
struct QuantityRequest
{
string stockSymbol;
};
interface StockInventoryService
{
CurrentQuantity getCurrent(in QuantityRequest request);
};
};
Декларация службы в WSDL
<?xml version="1.0" encoding="utf-8"?>
<definitions name="StockInventoryService"
xmlns: sqs="http://mycompany.com/stockinventoryservice.wsdl"
xmlns: sqsxsd="http://mycompany.com/stockinventoryservice.xsd"
xmlns: soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns: wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns: xsd="http://www.w3.org/2000/10/XMLSchema">
<wsdl: types>
<xsd: element name="CurrentQuantity">
<xsd: complexType>
<xsd: all>
<xsd: element name="stockSymbol" type="string"/>
</xsd: all>
</xsd: complexType>
</xsd: element>
<xsd: element name="CurrentQuantity">
<xsd: complexType>
<xsd: all>
<xsd: element name="quantity" type="float"/>
</xsd: all>
</xsd: complexType>
</xsd: element>
</wsdl: types>
<xsd: element name="CurrentQuantity">
<xsd: complexType>
<xsd: all>
<xsd: element name="quantity" type="float"/>
</xsd: all>
</xsd: complexType>
</xsd: element>
</wsdl: types>
<wsdl: message name="getCurrentInput">
<wsdl: part name="body" element="sqsxsd: CurrentQuantity"/>
</wsdl: message>
<wsdl: message name="getCurrentOutput">
<wsdl: part name="body" element="sqsxsd: CurrentQuantity"/>
</wsdl: message>
<wsdl: portType name="StockInventoryServicePortType">
<wsdl: operation name="getCurrent">
<wsdl: input message="sqs: getCurrentInput"/>
<wsdl: output message="sqs: getCurrentOutput"/>
</wsdl: operation>
</wsdl: portType>
<wsdl: binding name="StockInventoryServiceSoapBinding"
type="sqs: StockInventoryServicePortType">
<soap: binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl: operation name="getCurrent">
<soap: operation soapAction="http://mycompany.com/getCurrent"/>
<wsdl: input>
<soap: body use="literal"/>
</wsdl: input>
<wsdl: output>
<soap: body use="literal"/>
</wsdl: output>
</wsdl: operation>
</wsdl: binding>
<wsdl: service name="StockInventoryService">
<wsdl: port name="StockInventoryServicePort"
binding="sqs: StockInventoryServiceBinding">
<soap: address location="http://mycompany.com/StockInventoryService"/>
</wsdl: port>
</wsdl: service>
</definitions>
Третьим «усовершенствованием» стал отказ от автоматической подгрузки связанных объектов[84] в пользу исключительно ручного управления процессом.
В CORBA объекты функционируют на сервере, тогда как на клиенте находится только соответствующая заглушка (stub). То есть вы в программе вызываете какой-то метод, а на самом деле происходит обращение к серверу, вызов соответствующего метода у серверного объекта и возврат результата на клиента с возможным обновлением состояния локальных полей заглушки. Аналогично со свойствами объектного типа: связанный объект подгружается по мере необходимости. Всё это происходит прозрачно для программиста, которому не нужно вмешиваться в процесс взаимодействия, но желательно знать, что стоит за манипуляциями с заглушкой на клиенте. Соответственно, существует соблазн вместо реализации на сервере новой функции службы написать код непосредственно на клиенте, благо сделать это легко. Тогда, например, обработка достаточно большой коллекции объектов в цикле может вызвать интенсивный обмен сообщениями с сервером и возникновение узкого места в системе. Аналогичная проблема плохой реализации имеется и при работе приложения напрямую с СУБД.
В среде веб-сервисов вопрос с «нерадивым программистом» решили радикально – отменой самой возможности написать такой код. Несмотря на то что в 80 % случаев имевшаяся автоматическая загрузка была уместной и здорово сокращала программу.
Как уберечь кукурузу от насекомых-вредителей? Очень просто: выкосить её всю, к чертям. Вредители придут, а кушать нечего.
Возвращаемые веб-службами объекты не связаны с серверными за их отсутствием. Потому что у серверной части приложения нет состояния и, соответственно, не может быть никаких объектов в принципе. Обмен сообщениями происходит как и в обычной веб-среде: запрос – ответ без поддержки сессии. Общеупотребительная практика – использование DTO для передачи состояния объектов от клиента к серверу и обратно. Но DTO не содержит никаких ссылок на другие объекты, кроме вложенных. Его структура состоит из полей скалярных типов, разрешённых стандартом, вложенных объектов и массивов. Соответственно, вы не можете прозрачным образом динамически подгрузить недостающий объект, для чего придётся явным образом вызывать службу.
Прозрачная загрузка объектов в клиентском CORBA-приложении
BookGroup group = catalog.getBookCategory("Программирование");
Book[] books = group.getItems(); // один вызов сервера
foreach(Book book in books)
{
ShowInfo(book.Name +": ");
ShowInfo(book.getPopularity(). getVotesCount()); // два вызова
}
Работа клиентского приложения с DTO в среде веб-служб
BookGroupServiceClient groupClient = new BookGroupServiceClient(url1);
BookGroupDTO group = groupClient.GetBookCategory("Программирование");
BookServiceClient bookClient = new BookServiceClient(url2);
BookDTO[] books = bookClient.GetByGroupId(group.Id);
foreach(BookDTO book in books)
{
PopularityServiceClient popularityClient = new PopularityServiceClient(url3);
PopularityDTO popularity = popularityClient.GetByBookId(book.Id);
int votesCount = popularityClient.GetVotesCount(popularity.Id);
ShowInfo(book.Name +": ");
ShowInfo(votesCount);
}
Сила CORBA проявляется в том, что технология может работать и как в приведённых примерах, то есть с реализацией элементов полноценного многопоточного сервера приложений, и аналогично веб-службам, обрабатывая в сервисах объявленные в интерфейсах структуры, напоминающие DTO.
Вне контекста «автоматизированного бардака»[85] современные заявления о том, что СОА не оправдала возложенных на неё надежд, свидетельствуют о том, что и выбранная для неё модель веб-служб не стала решением проблем взаимодействия приложений в корпоративной среде. Ожидает ли нас новое пришествие CORBA в виде облегчённой её версии – покажет время. Поищите в Интернете по ключевым словам Web ORB – обнаружите немало интересного.
- Можно ли конструировать программы как аппаратуру?
- Безысходное программирование
- Эволюция аппаратуры и скорость разработки
- Диалог о производительности
- О карманных монстрах
- ASP.NET и браузеры
- Апплеты, Flash и Silverlight
- ООП – неизменно стабильный результат
- ORM, или объектно-реляционный проектор
- ВЦКП в облаках
- От CORBA к SOA
- Прогресс неотвратим