我們通常討論的CORBA模型往往從客戶機激發遠程方法的角度來討論C O R B A系統。象通常的客戶機和服務器模型:客戶機和服務器組件分別運行在不同的機器上,客戶機發出一個請求,然后客戶機阻塞。服務器主動監聽從客戶機來的請求。當收到一個請求后,服務器處理這個請求,并把結果返回給發出請求的客戶機??蛻魴C在等待回應時是阻塞的,只有在它接收到回答后才能繼續處理。
在很多場合中,這種模型正好是用戶所希望的。而且這種模型也符合傳統的C/S模型,有問有答。但是在很多場合中并不需要這種模型,象股票系統中,普通股民眼中的價格更新,SCADA電力系統中的現場數據的顯示等,都是不需要客戶機發出請求,服務器就把結果返回給客戶。還有在很多場合中,客戶機發出一個請求后,并不希望得到服務器的回答,它需要的只是給服務器一個通知,象SCADA電力系統中的前置機往服務器上發數據,前置機并不需要等待這個服務器上的回答返回。為解決上述問題,CORBA提出了ONEWAY以及事件服務異步傳輸機制。
ONEWAY異步傳輸,顧名思義,ONEWAY就是"單向",即客戶機發出它們的激發,然后繼續處理,而用不著在發出一請求后阻塞,直到結果返回,當服務器完成對該請求的處理后,它可以通過向客戶機發回一相應的單向激發把結?quot;返回",也可以不返回結果。
利用ONEWAY異步傳輸比較簡單,它的一般步驟與普通CORBA應用一樣:
1. 首先定義接口的IDL文件。
2. 編譯IDL文件。
3. 編寫服務器端程序。
4. 編寫客戶端程序。
這里我以DELPHI6(在DELPHI6中CORBA規范是由VisiBroker產品來實現的)為開發工具,來實現ONEWAY異步傳輸:
1. 首先定義接口的IDL文件:為了簡單,我這里只定義了一個ONEWAY的方法。
module Pro
{
interface IOnewDemo; interface IOnewDemo
{
oneway void onewaya(in long aa);
}; interface OnewDemoFactory
{
IOnewDemo CreateInstance(in string InstanceName);
};
};
2.和3在DELPHI6中,第二步和第三步是和在一起的。
選FILE-NEW-OTHER-CORBA-CORBA Server Application 在出現的IDL2Pas Create Server Dialog中按Add按鈕加入剛才定義的IDL文件,按OK按鈕以后,由IDL2Pas編譯器來對它進行編譯,生成Pro_c.pas,Pro-i.Pas,Pro_Impl.pas,Pro_s.pas四個文件,在Pro_Impl.pas文件中定義
procedure TIOnewDemo.onewaya ( const aa : Integer);
begin
form1.ListBox1.Items.Clear;
form1.ListBox1.Items.Add(inttostr(aa)); end;
在程序啟動時進行初始化
procedure TForm1.FormCreate(Sender: TObject);
var
Acct: IOnewDemo;
begin
CorbaInitialize;
// Add CORBA server code here like this
Acct := TIOnewDemoSkeleton.Create('ygl', TIOnewDemo.Create);
BOA.ObjIsReady(Acct as _Object); end;
編譯生成服務器端程序。
4. 編寫客戶端程序:
在程序啟動時進行初始化
procedure TForm1.FormCreate(Sender: TObject);
var
Acct: IOnewDemo;
begin
CorbaInitialize;
// Bind to the Corba server like this
Acct := TIOnewDemoHelper.bind;
end;
調用接口定義方法
procedure TForm1.Timer1Timer(Sender: TObject);//utilize the mothod of interface
var
i:integer;
begin
randomize;
i:=random(20);
acct.onewaya(i); end; 編譯生成客戶器端程序。
CORBA中的事件服務是基于以下的原因定義的:
事件發送者和事件接收者的關系是松偶合的:事件發送者發送消息時,并不關心誰會收到消息。事件接收者接收消息時,也并不關心誰發送的消息。這里事件發送者稱為事件供應者,事件接收者稱為事件消費者。
在VisiBroker的事件實現中,它定義了兩種事件模型:PUSH模型和PULL模型:如果一個事件是由供應者主動發出,消費者被動接收,就稱為PUSH模型,如果一個事件是由消費者主動索取,供應者被動提供,就稱為PULL模型。
PUSH模型可以簡單表示為:
DELPHI6中在COSEVENT單元中定義了通用的接口:
PushConsumer , PushSupplier, PullSupplier , PullConsumer, ProxyPushConsumer
ProxyPullSupplier , ProxyPullConsumer , ProxyPushSupplier , ConsumerAdmin , SupplierAdmin , EventChannel…..
這里我用PUSH模型簡單說一下通訊建立的過程:
供應者端:
1. 供應者獲得一個SupplierAdmin對象。供應者通過調用Event_Channel的for_suppliers得到一個SupplierAdmin對象。
2. 供應者獲得一個ProxyPushConsumer對象。供應者通過調用SupplierAdmin的obtain_push_consumer得到一個ProxyPushConsumer對象。
3. 供應者利用ProxyPushConsumer對象的connect_push_supplier連接遠方的ProxyPushSupplier對象。
消費者端:
1. 消費者獲得一個ConsumerAdmin對象。消費者通過調用Event_Channel的for_consumers得到一個ConsumerAdmin對象。
2. 消費者獲得一個ProxyPushSupplier對象。消費者通過調用ConsumerAdmin的obtain_push_supplier得到一個ProxyPushSupplier對象。
3. 消費者利用ProxyPushSupplier對象的connect_push_consumer連接遠方的ProxyPushConsumer對象。
在DELPHI6中對事件服務的PUSH模型實現的例子如下:
供應者端:
實現TpushSupplier類
TPushSupplier = class(TInterfacedObject, PushSupplier)
public
constructor Create;
procedure disconnect_push_supplier;
end;
主程序:
procedure TForm1.FormCreate(Sender: TObject);
var
PushSupplier_Skeleton,PushSupplier_Skeletonaaa : PushSupplier;
Event_Channel,Event_Channelaaa : EventChannel;
Supplier_Admin,Supplier_Adminaaa : SupplierAdmin;
Push_Consumer,Push_Consumeraaa : ProxyPushConsumer;
myAny:any;
begin
CorbaInitialize;
// Create the skeleton and register it with the boa
PushSupplier_Skeleton:=TPushSupplierSkeleton.Create('ygl', TPushSupplier.Create);
BOA.SetScope( RegistrationScope(1) );
BOA.ObjIsReady(PushSupplier_Skeleton as _Object);
//bind to the event channel and get a Supplier Admin object
獲得事件信道接口,bind的參數是下面命令行中的參數ygl:
Event_Channel := TEventChannelHelper.bind('ygl');
//可以連接另一個事件信道bind的參數是另一個實例中的參數yglaaa:
// Event_Channelaaa := TEventChannelHelper.bind('yglaaa');
獲得SupplierAdmin接口:
Supplier_Admin := Event_Channel.for_suppliers;
//get a push consumer and register the supplier object
獲得ProxyPushConsumer接口:
Push_Consumer := Supplier_Admin.obtain_push_consumer;
連接ProxyPushSupplier接口 Push_Consumer.connect_push_supplier(PushSupplier_Skeleton);
往信道中推數據
randomize;
myany:= random(1000);
try
Push_Consumer.Push(myAny);
except
on EDisconnected do ShowMessage('Client Disconnected');
end;
end;
消費者端:
實現TPushConsumer類
TPushConsumer = class(TInterfacedObject, PushConsumer)
public
constructor Create;
procedure push(const data : Any);
procedure disconnect_push_consumer;
end;
這里,重要的方法是:
procedure TPushConsumer.push(const data : Any);
var num :integer
begin
num := data;
Form1.ListBox1.Items.Add(IntToStr(num));
end;
它表示消費者收到數據后的處理。
主程序:
procedure TForm1.FormCreate(Sender: TObject);
var
PushConsumer_Skeleton : PushConsumer;
Event_Channel : EventChannel;
Consumer_Admin : ConsumerAdmin;
Push_Supplier : ProxyPushSupplier;
begin
CorbaInitialize;
// Create the skeleton and register it with the boa
PushConsumer_Skeleton:=TPushConsumerSkeleton.Create('ygl', TPushConsumer.Create);
BOA.SetScope( RegistrationScope(1) );
BOA.ObjIsReady(PushConsumer_Skeleton as _Object);
獲得事件信道接口:
//bind to the event channel and get a Supplier Admin object
Event_Channel := TEventChannelHelper.bind('ygl');
獲得ConsumerAdmin接口:
Consumer_Admin := Event_Channel.for_consumers;
//get a push consumer and register the supplier object
獲得ProxyPushSupplier;接口:
Push_Supplier := Consumer_Admin.obtain_push_supplier;
連接ProxyPushConsumer接口
Push_Supplier.connect_push_consumer(PushConsumer_Skeleton);
end;
程序運行:
首先運行SmartAgent,再在…\VisiBroker\bin下以命令行方式運行channel ygl 然后分別運行供應者端,消費者端程序。
在這里要注意兩個方面的問題:
1.由于大多數O R B使用T C P作為它們底層的傳輸,而T C P是一可靠的協議,所以即使用戶認為是非阻塞的調用,實際上在T C P層還是阻塞的調用。用戶的客戶機應用程序可能不等待服務器應用程序接收并開始處理請求,但是客戶機的T C P / I P棧卻要等待,直到請求緩沖區被服務器的T C P / I P棧成功接收(和確認)。
2. 在Visibroker中并沒有提供事件粒度的控制-過濾功能。
作者簡介:
于國良,男,1969年12月出生,碩士,系統分析員。郵件地址:ygl6528@sohu.com
原文轉自:http://www.anti-gravitydesign.com