Статья опубликована в журнале MSDeveloper.RU
В февральской статье мы рассмотрели основные компоненты для программирования под BizTalk Server (здесь и далее BTS), выбрав довольно простую задачу по сложению двух цифр. Для этой статьи изменим формулировку задачи, введем элементы интеграции систем, т.е. то, для чего, собственно, и был разработан BTS.
Содержание
Задача
Думаю, вы уже сталкивались с системами, которые связаны друг с другом по принципу «каждый с каждым», т.е. каждая система напрямую связана с любой другой. Минусы такой организации взаимодействия можно легко назвать навскидку. В первую очередь, это трудности по реализации взаимодействия с каждой конкретной системой. Если, например, в комплексе штук 30 систем, привязываться по определенным протоколам к другим 29 системам очень и очень накладно. К тому же, если одна система изменит какой-то свой формат передачи данных, то всем остальным системам нужно будет поменять свои внутренние реализации взаимодействия.
Для решения трудностей по интеграции вполне логичным выглядит решение завести центральный компонент, который будет связывать все остальные системы. Плюсы такого подхода очевидны – каждая из систем должна уметь взаимодействовать только с этим центральным компонентом, таким образом связность системы в целом уменьшается в разы.
Теперь перейдем к нашему проекту. Предположим, что сам BTS не умеет решать задачу «сложить числа A и B». Это, конечно, смешно и неправда, но под задачу сложения двух чисел можно подставить любую другую. На данный момент нам нужно, чтобы результаты вычислений брались из другой системы. В реальных системах под такой задачей может стоять получение каких-то данных, например, справочников или результатов каких-то сложных вычислений.
Определимся с участниками нашего процесса:
- Шина
- Модуль User, запрашивающий информацию у шины
- Модуль CalcService, который умеет считать результат
Можно нарисовать диаграмму последовательности работы по процессу:
Модуль User запрашивает информацию у шины, шина делегирует запрос сервису CalcService, который этой информацией обладает, ждет ответа от этого сервиса и возвращает затем этот ответ к модулю User.
Итого нам нужно разработать 2 веб-сервиса и шину. Начнем с написания заглушки к шине.
Решение
В данном разделе будет описываться создание интеграционной шины с использованием Microsoft Visual Studio 2010. Я предполагаю, что читатель ознакомился с предыдущей февральской статьей, а также умеет создавать WCF-сервисы.
Создание заглушки шины
Откроем Visual Studio 2010 от имени администратора, создадим новый проект EsbSample.EsbCore, студия, как обычно, сгенерирует заготовку решения и проекта.
Общий вид оркестровки
Сейчас нужно закодировать процесс, для этого в сборку EsbSample.EsbCoreнужно добавить новую оркестровку CalcProcess. После добавление оркестровки можно набросать сценарий его работы:
Его можно прочитать следующим образом:
— Receive_1 получает запрос на сложение двух чисел
— Send_1 отправляет запрос сервису сложения чисел
— Receive_2 получает результат сложения
— Send_2 отправляет результат сложения
Сейчас нам нужно указать логические входящие и исходящие порты для сообщений, но вначале нам нужно создать схемы сообщений, которые мы будем использовать.
Схемы
Запрос
Добавим в проект схему для запроса (EsbRequest) и создадим следующую структуру:
Вот ее код:
<?xml version="1.0" encoding="utf-16"?> <xs:schema xmlns="http://EsbSample.EsbCore.EsbRequest" xmlns:b="http://schemas.microsoft.com/BizTalk/2003" targetNamespace="http://EsbSample.EsbCore.EsbRequest" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Request"> <xs:complexType> <xs:sequence> <xs:element name="A" type="xs:int" /> <xs:element name="B" type="xs:int" /> <xs:element name="CorrelationID" type="GUID" /> </xs:sequence> </xs:complexType> </xs:element> <xs:simpleType name="GUID"> <xs:restriction base="xs:string"> <xs:pattern value="[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" /> </xs:restriction> </xs:simpleType> </xs:schema>
Ответ на запрос
Аналогичным образом добавим еще одну схему, EsbResponse, следующего вида
<?xml version="1.0" encoding="utf-16"?> <xs:schema xmlns="http://EsbSample.EsbCore.EsbResponse" xmlns:b="http://schemas.microsoft.com/BizTalk/2003" targetNamespace="http://EsbSample.EsbCore.EsbResponse" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Response"> <xs:complexType> <xs:sequence> <xs:element name="Result" type="xs:int" /> <xs:element name="CorrelationID" type="Guid" /> </xs:sequence> </xs:complexType> </xs:element> <xs:simpleType name="Guid"> <xs:restriction base="xs:string"> <xs:pattern value="[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" /> </xs:restriction> </xs:simpleType> </xs:schema>
Логические порты
Создание типов логических портов
Итак, у нас определены схемы сообщений, сейчас можно создавать логические порты. Для этого вначале создадим два типа портов, один для запроса, второй для ответа. Создание типа порта для запроса начинается в окне Orchestration View, нужно найти узел Port Typesи через контекстное меню вызвать команду создания типа порта:
Эта команда создаст заготовку для типа порта, у нее через окно Propertiesнужно задать необходимые свойства для создания следующей структуры:
Здесь я создал тип порта под названием RequestPortType, c одной операцией Calc и типом запроса EsbRequest.
Аналогичным образом создается тип порта для ответов:
Таким образом, у нас появилось два типа порта, теперь нам нужно создать сами порты.
Создание портов
Для Receive_1 создадим порт InboundPortсо следующей конфигурацией:
Для SendPort_1 конфигурация порта будет аналогично, только на экране Port Binding нужно выбрать Port direction of communication: I’ll always be sending messages on this port.
Для Receive_2 конфигурация будет выглядеть вот так:
Для Send_2 создание порта аналогичное, только на экране Port Binding нужно указать Port direction of communication: I’ll always be sending messages on this port.
После создания портов нужно аккуратно соединить соответствующие коннекторы, чтобы получилась следующая картина:
Обращаю внимание на то, что, когда соединяется порт и фигура Receive, автоматически создается сообщение, в то время как при соединении фигур Send сообщения не создаются. Поэтому в окне Orchestration View в узле Messages нужно создать два сообщения типа EsbResponse:
Таким образом, у нас должно получиться 4 сообщения, нужно их переименовать и установить в каждой фигуре Receive\Send соответственно таблице:
Название | Тип | Название фигуры |
RcvdMsg_1 | EsbRequest | Receive_1 |
RcvdMsg_2 | EsbResponse | Receive_2 |
SntMsg_1 | EsbRequest | Send_1 |
SntMsg_2 | EsbResponse | Send_2 |
Пример установки сообщения для фигуры:
Если вы все сделали верно, то получится следующая картинка:
Заметим, что иконка с восклицательным знаком осталась только у фигур ConstructMessage. Давайте займемся ими, выделяем ConstructMessage_1, выбираем его свойства и указываем, что мы будем создавать сообщение SntMsg_1
После этого добавляем в приложение две карты (map), с названиями CopyRequest и CopyResponse. Как нетрудно догадаться из названий карт, каждая из них будет просто копировать из одного сообщения в другое, сообщения должны быть одинакового типа. Пример для CopyRequest:
После того как карты были созданы, можно удалить фигуры Message Assignment, которые располагаются внутри фигур Construct Message и поставить туда взамен фигуры Transform:
Затем нужно два раза щелкнуть по Transform_1 (откроется окно Transform Configuration) и установить следующие параметры:
— Existing Map(Fully Qualified Map Name=”EsbSample.EsbCore.CopyRequest”)
— Transform Source: RcvdMsg_1
— Transform Destination: SntMsg_1
Для фигуры Transform_2 нужно совершить аналогичные действия:
— Existing Map(Fully Qualified Map Name=”EsbSample.EsbCore.CopyResponse”)
— Transform Source: RcvdMsg_2
— Transform Destination: SntMsg_2
После всех этих манипуляций нужно у фигуры Receive_1 установить свойство Activate = True– и оркестровка почти готова
Корреляции
Вернемся к описанию нашего процесса. Итак, шина получает запрос на вычисление, адресует это вычисление веб-сервису, а затем ждет сообщения с ответом. Допустим, вычисление результата занимает какое-то время, скажем, минут 10. В течении этого времени к нам придет еще один запрос на вычисление, мы также делегируем его сервису и ровно так же будем ждать ответа. Здесь возникает вопрос – когда к нам придет сообщение с ответом, ответом на какой запрос оно будет являться, на первый или на второй? Непонятно. Для этого было введено понятие корреляции, т.е. соответствия сообщений. В наших схемах для корреляции выделено поле CorrelationID.
Работает это следующим образом: нам приходит первый запрос с CorrelationID= «cfc2e57b-63fe-44c8-b071-047e1a34b413», мы перенаправляем этот запрос веб-сервису. И затем ждем сообщения с ответом, где в поле CorrelationID будет стоять значение «cfc2e57b-63fe-44c8-b071-047e1a34b413». Соответственно, если нам придет второй запрос, то CorrelationIDу него будет другим, и путаницы в этом случае уже не возникнет.
Для настройки корреляции нам нужно немного поправить схемы. Начнем с EsbRequest, открываем его в редакторе, выбираем поле CorrelationID и в контекстном меню выбираем команду Promote \ Quick Promotion. В проект добавится файл PropertySchema.xsd, в нее добавится наше свойство. Аналогичные действия нужно совершить с EsbResponse.
Затем переходим обратно в оркестровку, в окне Orchestration View выбираем узел Correlation Typesи создаем новый тип корреляции:
Находим в списке Available Properties нашу схему со свойствам, выбираем CorrelationID, нажимаем кнопку Add:
Получился тип корреляции, завязанный на поле CorrelationID:
Затем в окне Orchestration View выбираем узел Correlation Sets, добавляем новый набор корреляции и указываем в качестве ее типа только что созданный:
После этого нам в оркестровке нужно где-то «запомнить», инициализировать корреляцию. Если взглянуть на оркестровку, можно заметить одно место, где она нам пригодится – когда мы ожидаем ответа от веб-сервиса по рассчету данных, это фигура Receive_2, в нее мы ждем сообщения именно с тем идентификатором корреляции, который был отправлен ранее в сообщении Send_1. Поэтому выделяем фигуру Send_1 и устанавливаем у нее свойство Initializing Correlation Sets:
А у фигуры Receive_2 устанавливаем свойство Following Correlation Sets:
Таким образом, при отправке сообщения через Send_1 мы запоминаем CorrelationID и затем в Receive_2 ждем сообщения, у которого CorrelationIDбудет таким же. Всё просто.
Сейчас можно попробовать собрать решение. Если вы всё сделали верно, а я ничего не забыл описать, билд пройдет успешно.
Развертывание приложения
Сейчас нам нужно развернуть приложение. Для этого вначале нужно открыть свойства проекта, подписать сборку на вкладке Signing, а затем задать настройки развертывания на вкладке Deployment (всё это было описано в прошлой статье). Название у приложения будет «Esb». После этого нужно выбрать в меню Build – Deploy Solution
Если вы всё сделали правильно, в окне Output будет сказано об успешном развертывании приложения. А если нет, пишите письма, разберемся.
На данном шаге я предлагаю прерваться и продолжить в следующей части.
Что дальше
В данной статье мы разобрали, как создавать оркестровки, которые будут обращаться к другим системам. В следующей статье я планирую разобрать пример с созданием веб-сервиса, который будет заниматься вычислениями и возвращать ответ шине, а также покажу некоторые нюансы при вызове веб-сервисов из BTS.
Исходники проекта доступны по ссылке http://goo.gl/OZPI9
Жаль, что продолжения нет. Хотелось бы ещё увидеть, как можно свои вырожения разрабатывать! Спасибо!
Жаль, что нет информации о настройки портов. И ещё хотелось бы почтитать о создании своих выражений на C#. Жду продолжения, спасибо за хорошие статьи.
Здравствуйте!
Выражения свои разработать нельзя, вы путаете с WWF и их Custom Activities.
Для кастомных выражений в бизтолке используется фигура Expression, где вы можете писать код, в том числе дергающий классы и методы из обычных .NET-сборок (не забудьте поместить их в GAC)