oldi

Распределенные вычисления и технологии Inprise

Часть 5. Cоздание CORBA-серверов, переносимых на другие платформы, с помощью С++Builder 4

Наталия Елманова

Данная статья посвящена возможностям C++Builder 4, связанным с созданием распределенных многоплатформенных систем. Это вторая часть опубликованного обзора, посвященного возможностям С++Builder 4 в предыдущем выпуске журнала. обзора, посвященного возможностям С++Builder 4.

Постановка задачи

Создание переносимого сервера функциональности

Создание клиента с графическим пользовательским интерфейсом

Создание переносимого клиентского приложения

Несколько слов о серверах доступа к данным

 

Организация распределенных вычислений в последние два года стала «модным» направлением в области проектирования информационных систем. Весьма популярны распределенные системы на основе COM и MIDAS (возможность создания которых появилась в предыдущей версии С++Builder). Однако нередко (особенно в случае повышенных требований к надежности подобного рода систем) возникает потребность использовать в качестве серверной части такой распределенной системы приложения, функционирующие на платформе, отличной от Windows (что исключает использование COM как специфичной для Windows технологии).

Следует отметить, что C++Builder позволяет генерировать код для таких переносимых приложений (естественно, за счет отказа от использования COM, VCL и Windows API). Соответственно, в качестве технологии распределенных вычислений в этом случае применяется многоплатформенная технология, не использующая специфику Windows. В последнее время для этой цели наиболее часто используется CORBA (Common Object Request Broker Architecture), спецификация которой создана консорциумом Object Management Group. В комплект поставки C++Builder Enterprise входит набор утилит и сервисов, представляющих собой одну из реализаций этой спецификации — Inprise VisiBroker.

Данная часть обзора возможностей C++Builder 4 описывает создание сервера функциональности с использованием VisiBroker, переносимого на другие платформы, а также создание клиентских приложений для него (в том числе переносимых на другие платформы).

Постановка задачи

Создадим приложение, производящее, к примеру, сложные научные вычисления (в нашем примере это вычисление значения некоторой функции, представляющей собой полином третьей степени). В реальной жизни вместо этого расчета может реализоваться любая другая функциональность — доступ к данным, обработка сигналов, поступающих с аппаратуры, генерация отчетов и т.д..

Пусть наше приложение имеет главную форму следующего вида (рис. 1):

В нашем примере компонент TChart содержит одну серию (ее следует добавить вручную при создании формы).

Создадим обработчик события, связанный с нажатием на кнопку с надписью «График», и в этом же модуле создадим реализацию нашей функции:

//---------------------------------------------------------------------------
 #include <vcl.h>
 #pragma hdrstop
 #include "Unit1.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 #pragma resource "*.dfm"
   TForm1 *Form1;
//---------------------------------------------------------------------------
   __fastcall TForm1::TForm1(TComponent* Owner)
   : TForm(Owner)
   {
   }
//---------------------------------------------------------------------------
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
   {
     int i; double x1,y;
       for (i=1;i<60;i++)
       {
         x1=0.1*float(i-13);
         y=fun1(x1);
        Chart1->Series[0]->AddXY(x1,y,FloatToStr(x1),clWhite);
         }
     }
       double fun1(double x)
       {
         double r=x*x*x-5*x*x+3*x+5;
         return(r);
        }


//---------------------------------------------------------------------------

Результатом работы такого приложения будет график нашего полинома, появляющийся при нажатии на кнопку «График» (рис. 2):

Это обычное Windows-приложение, содержащее в себе и пользовательский интерфейс, и функциональность, связанную с проведением расчетов (вычисление значений функции).

Теперь попробуем разбить это приложение на две части, отделив пользовательский интерфейс (кнопки и компонент TChart) от функциональности. Иными словами, мы создадим сервер функциональности, вычисляющий значение функции, и клиентское приложение, использующее этот сервер как поставщика результатов расчетов.

В начало

В начало

Создание переносимого сервера функциональности

Итак, создадим CORBA-сервер, осуществляющий проведение расчетов. Сразу же заметим, что подобного рода серверы, как правило, не нуждаются в пользовательском интерфейсе, поэтому его вполне можно создать как консольное приложение. Если при этом мы откажемся от использования VCL, код полученного сервера может быть скомпилирован любым другим компилятором C++ (в том числе, естественно, и компилятором для другой платформы). Консольные приложения — единственный тип приложений, существующий для любых платформ, в отличие от приложений с графическим интерфейсом пользователя. (Последние требуют графических библиотек или операционных систем с GUI, и на все эти графические библиотеки и функции API подобных операционных систем нет никаких стандартов, в отличие от самого языка С++).

Иными словами, сервер может быть создан переносимым на другие платформы, что и будет сделано.

Для создания сервера выберем из репозитария объектов со страницы Multitier пиктограмму CORBA server (рис. 3):

После выбора пиктограммы в диалоговой панели CORBA Server Wizard выберем в качестве типа приложения Console Application и отключим возможность использования VCL. Это позволит нам создать переносимый на другие платформы код:

Первое, с чего следует начинать создание распределенной системы, — это описание интерфейсов сервера. С этой целью используется язык IDL (Interface Definition Language), являющийся, по существу, стандартом для подобного рода описаний, не зависящим от языков программирования и платформ. Отметим, что существует несколько диалектов IDL (COM IDL, CORBA IDL, DCE IDL), имеющих некоторые различия. Естественно, мы будем использовать CORBA IDL. Так как мы еще не создали никакого IDL-описания, выберем опцию Add New IDL File. После нажатия на кнопку OK в окне редактора кода появится пустая страница для ILD-описания, куда можно ввести описание интерфейса нашего сервера.

interface b1
{
 double fun1(in double x);
 } ;

Далее следует скомпилировать IDL-файл. В действительности это не компиляция, а кодогенерация, в результате которой мы получим два модуля: fun1_s.cpp and fun1_c.cpp, содержащие stub-код и skeleton-код.

Что же представляют собой stub-объект и skeleton-объект?

Все способы взаимодействия между серверами функциональности и их клиентами основаны на механизме вызовов удаленных процедур (RPC — Remote Procedure Calls) и маршаллинга, представляющего солой обмен пакетами данных между объектом внутри клиента (stub-объектом) и объектом внутри сервера (skeleton-объектом). Skeleton-объект является представителем клиента внутри адресного пространства сервера. Обращаясь к нему, сервер «думает», что он имеет дело с локальным объектом. Stub-объект является представителем сервера внутри адресного пространства клиентского приложения, поэтому последнее также «думает», что вызывает методы локального объекта. В действительности так оно и есть, однако вместо истинной реализации (в нашем случае — вычисления значений функции) методы этого объекта осуществляют вызовы удаленных процедур и обмен пакетами данных с сервером (рис. 5):

Теперь можно вспомнить о реализации нашей функции. Для этого выберем со страницы Multitier репозитария объектов пиктограмму CORBA Object Implementation. В диалоговой панели CORBA Object Implementation Wizard выберем имя интерфейса (b1), а также определим имя модуля, содержащего реализацию, и имя класса CORBA-объектов. В данном примере мы опишем объект, экземпляр которого создается в момент старта сервера, так что сервер сразу же сможет принимать обращения клиентов (рис. 6):

Мы можем просмотреть изменения, внесенные в наше приложение этим экспертом. В частности, имеет смысл внести в код реализации нашего метода строки, ответственные за проведение расчетов (рис. 7):

Затем можно скомпилировать проект и закрыть его.

Перед созданием клиента мы должны запустить VisiBroker Smart Agent — службу каталогов CORBA, позволяющую осуществить доступ к CORBA-серверам. Теперь можно запустить наш сервер (желательно отдельно от среды разработки).

Можно создавать клиенские приложения.

В начало

В начало

Создание клиента с графическим пользовательским интерфейсом

Для начала создадим клиентское приложение, представляющее собой обычное Windows-приложение с интерфейсом, похожим на представленный на рис.2. Для этого выберем пиктограмму CORBA Client со страницы Multitier репозитария объектов C++Builder.

В этом примере мы будем использовать раннее связывание клиента с сервером (в этом случае можно достичь максимального быстродействия). Для этого выберем из меню среды разработки опцию «Edit/Use CORBA object». В соответствующем эксперте мы должны добавить к проекту тот же самый IDL-файл, что был создан при создании сервера, а также ответить на вопросы об именах объектов и переменных, в частности, об имени нового свойства формы, к которому следует обращаться, если нужно вызвать метод сервера (рис. 8):

Далее можно создать интерфейс, сходный с приведенным на рис. 1, и создать обработчик события, связанного с нажатием на кнопку «График»:

void __fastcall TForm1::BitBtn1Click(TObject *Sender)
 {
   int i; double x1,y;
   for (i=1;i<60;i++)
    {
      x1=0.1*float(i-13);
      y=b1_1->fun1(x1);
      Chart1->Series[0]->AddXY(x1,y,FloatToStr(x1),clWhite);
     }
 }

В результате получим тот же график, что и на рис.2.

В начало

В начало

Создание переносимого клиентского приложения

Теперь создадим клиентское приложение, переносимое на другие платформы. Это должно быть консольное приложение, не использующее VCL. В этом случае при создании клиента мы должны выбрать опцию «Console Application» и отменить опцию «Enable VCL» .

Все остальные действия похожи на предыдущие, за исключением создания пользовательского интерфейса. Простейший способ создать пользовательский интерфейс в данном случае — вывести результаты расчетов на экран и поместить этот код непосредственно в файл проекта:

//---------------------------------------------------------------------------


#include <corbapch.h>
#pragma hdrstop

//---------------------------------------------------------------------------
#include "fun1_c.hh"

#include <corba.h>
#include <condefs.h>
USEIDL("corba\corba_rus\fun1.idl");
USEUNIT("corba\corba_rus\fun1_c.cpp");
USEUNIT("corba\corba_rus\fun1_s.cpp");
//---------------------------------------------------------------------------
#pragma argsused
int main(int argc, char* argv[])
   {
     try
       {
         // Initialize the ORB and BOA
        CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
        CORBA::BOA_var boa = orb->BOA_init(argc, argv);
        a1_var a1_1 = a1::_bind("a1Obj");

cout<<"Our function table \n";
int i; double x1,y;
for (i=1;i<271;i++)
   {
    x1=0.1*float(i);
    y=a1_1->fun1(x1);
    cout<<x1<<" "<<y<<"\n";
    }
       }
     catch(const CORBA::Exception& e)
      {
       cerr << e << endl;
       return(1);
        }
       return 0;
 }
   //---------------------------------------------------------------------------

В этом случае мы получим таблицу с результатами расчетов непосредственно на экране консольного приложения.

Отметим, что данное приложение можно скомпилировать любым компилятором С++, в том числе и компилятором для платформы, отличной от Windows.

В начало

В начало

Несколько слов о серверах доступа к данным

Серверы доступа к данным являются одним из наиболее популярных типов серверов middleware. Как создать переносимый сервер доступа к данным?

Наиболее популярный способ создания таких серверов — использование MIDAS и создание удаленных модулей данных как COM- или CORBA-объектов. В этом случае мы получим COM- или CORBA-сервер, но это будет Windows-приложение, так как оно использует VCL, основанную на Windows API, и BDE, которая также представляет собой набор Windows-библиотек. Создание таких серверов будет более подробно рассмотрено в следующей статье данного цикла.

Если нам нужен переносимый сервер доступа к данным, он не должен использовать ни VCL, ни BDE. Как быть в этом случае?

Вспомним, что вся функциональность сервера доступа к данным — это набор SQL-запросов, и ничего более. Для MIDAS-серверов генератором запросов является BDE. Но выполнить запрос можно и другим способом! Например, всегда есть возможность использования низкоуровневого API клиентских частей серверных СУБД (например, Oracle Call Interface). Для большинства серверных СУБД такие API существуют для многих платформ и содержат одни и те же функции. Использование таких API может показаться несколько утомительным, но в целом оно ненамного сложнее, чем написание других функций.

Российское представительство Inprise:

Тел.: 7(095)238-36-11

http://www.inprise.ru

e-mail: info@inprise.ru

КомпьютерПресс 5'1999