从技术的角度来讲Q把pȝ设计成n层结构需要解决系l管理上的一些问题,如网l的延时Q系l的反应旉Q服务的可用性,负蝲的管理,分布式的~冲和分布式的垃圑֛收等。而且Q对于每一个能提高pȝ效率的新的解x案,也会随之带来新的问题。但是这些在设计大型的分布式应用pȝ的技术上的问题,都可以通过使用一些基本的设计Ҏ和技巧来加以解决?/TD> |
通过使用q代QIteratorQ的设计模式来定义idl语言Q从而解决corbaE序中诸如性能理Q缓Ԍ分布式垃圑֛收等问题?/TD> |
一Q性能上的问题 |
虽然corba的体pȝ构简化了|络的内在的复杂性。但它不能保证一定可以构造一个高 |
性能Q高效率的系l,要实现这个目标,整个pȝ的设计一定要考虑到网l固有的l构Q主要是以下的三个因素?/TD> |
1Q?q程调用的数量?/TD> |
2Q?数据传输的数量?/TD> |
3Q?不同数据cd的{换和包装?/TD> |
如果在系l设计的开始加以考虑Q这些问题将会得到解冟?/TD> |
在基于corba的体l设计中Qidl在组件的设计中v了很大的作用Q应为它定义了服务端E序怺遵@的接口标准?/TD> |
二.IDL的设?/FONT> |
一个通常在IDL设计中被忽视的问题是哪一个接口用于服务器端的应用E序Q以及暂时的QtransientQ和持久的(persisentQcorba对象?/TD> |
1Q一个服务器端的应用E序是一个用于实现对象方法的Q与语言无关的对象。在corbaE序的模型中Q服务器端的E序通过可移植对象适配器(Portable Object Adaptor,即POAQ向pȝ注册Q从而能一直接受用户对它的调用?/TD> |
2Q同服务器端的对象相比,暂时的corba对象q不用POA向系l注册,他们在用户向pȝh的过E中由服务器端的应用E序生成。这些暂时的corba对象的生命周期不会超q所在进E或生成该对象的U程的生命周期,而且他们的对象句柄ƈ不公开?/TD> |
3Q持久性的corba对象同持久性的状态相兌Q有着Ҏ的用途?/TD> |
以下主要讨论使用暂时的corba对象来管理大量数据的传输。这个方法在处理可能丢失数据的程序时非常有用Q如下例所C: |
用户要查询大量的数据Q在得到了前20个数据后Q另一个用户也提出一个查询,可能的情冉|前面查询的数据将丢失。在一个单q程的应用程序中Q这不是一个问题。但是在分布式编E和设计中。将会占用很多的|络带宽和cpu的处理时间?/TD> |
Z如上所qͼ图一提供了一个客L和服务器端程序的交互相应的示意图。客L的代理是个远端的代理c,用于处理同远端服务器E序的连接以及把客户端的h发送到服务器端。客L向提供所需服务的服务器端付求,服务器端q回一l品的信息Q如果这个结果中有n个品,则N个元素经q参数{换后在返回。初一看,q个设计是可行的Q如果不发生前面所说的不可预料的情늚话?/TD> |
|
下面的代码定义了用来实现客户端和服务器端交互作用的IDL接口和方法。该文g中只定义了一个方法,用来q回一l品的信息?/TD> |
Module productcatalog |
{ |
struct ProductItem |
{ |
string productName; |
?/TD> |
}; |
typedef sequence |
Interface ProductCatalog |
{ |
ProductItemList getProductItems(in string group,in string category,in string status ) |
Raises {SomeRemoteException}; |
}; |
}; |
从客L的角度看Q对服务器端E序的一个请求,来检索品的信息需要如下的几步骤。如图二所C? |
|
代理的对象提供了一个包Q绑定到一个特定的服务器端的对象实例上来充当一个中间的对象Q用来管理对q程服务器对象的调度了管理。它的主要Q务包括: |
1Q?准备一个请?/TD> |
2Q?把调用请求提交给ORB的桩(stub) |
3Q?捕捉QtrapQ远端的异常。(最好把它们映射到用戯己定义的异常中) |
4Q?准备好用于接受特定对象模型的数据l构。这些数据要被客L的应用程序用品类代理的实现如下面的代码所C: |
public class ProductServiceProxy |
{ |
private static ProductServiceProxy instance; |
private ProductServiceProxy() {} |
public static ProductServiceClient getInstance() |
{ |
if(instance==null) |
instance=new ProductServiceProxy(); |
return instance; |
} |
public ArrayList getProducts(String group,String category,String status) |
throws SomeException |
{ |
ArrayList productList=new ArrayList(); |
Try |
{ |
ProductService productService=LocateRemoteService(); |
If(productService==null) |
//return empty list or throw user defined exception |
} |
try |
{ |
//get products using criteria,when request returns data,prepare data and store |
results in collection object |
} |
catch(SystemException se){//handle exception } |
} |
catch(Exception e){//handle exception} |
return productList; |
} |
} |
客户端的代理cȝ单一QsingletonQ模式来实现Q从而可以保证只有一个客L的代理对象实例生成,对于每一个需要远端调用的hQLocateRemoteService()Ҏ׃调用Q从而来验证q端对象的句柄,如果q接已断开或者对象的句柄已经失效Q就会自动绑定,从而能防止p|?/TD> |
另外的一UIDL的设计是Q服务器端程序返回一pd的品ID可不是品本w?/TD> |
如图三所C: |
|
ql本w来Ԍq两个设计的差别主要在于产品IDL数据量要q远于产品本n的数据量。而用户也可以选择其中的一个IDh察看产品的详l信息。这个设计同前面的设计相比更合理。因为它能根据用L需求来传送数据,从而不会导致大量无用数据的传输?/TD> |
但是q个设计Ҏ也有一个弊端,卌军_是否要显CZ品ID受而检索每一产品都需要查询所有的产品信息Q这样就会带来同W一个方案相同的问题。因Zؓ了数据,需要进行n+1ơ的q程调用?/TD> |
W三U方案如囑֛所C,它主要重新设计了IDLQ利用用L习惯和系l计的限制。这个设计上的改q主要基于如下的考虑?/TD> |
|
对于很多的用L面的应用E序来讲Q如ZJAVA SWING或WEB的程序,一ơ现实多数据常怼受到屏幕大小的限制。对与WEB的程序来Ԍ查询的结果常 怼被分为很多页Q由D栏来控制信息的显C。而且Q一般来_用户常常需要一?的时间来览昄的信息,然后才会选择下一步的动作。这qE序的运行了调用带来 了一定意见的~冲?/TD> |
Z实现q个目的Q需要运用如下的机制来规范信息的动了传输。一个方法就是用P代(IteratorQ设计模?因此需要重新设计IDLQ从而能使大量的数据分成?个块来传输。ؓ了方便程序的实现Q需要首先定义一个基本的q代接口来规定远EP?cȝҎ。下面的代码定义了一个基本的BaseIterator和BaseListIterator接口?/TD> |
/* Base Iterators in IDL */ |
#include “util/exceptions/idlexceptions.idl?/TD> |
module iterator |
{ |
interface BaseIterator |
{ |
Boolean hasNext() raises(SomeRemoteException); |
Short count() raises(SomeRemoteException); |
}; |
interface BaseListIterator |
{ |
Boolean hasPrevious() raises(SomeRemoteException); |
Short previousIndex() raises(SomeRemoteException); |
Boolean hasNext() raises(SomeRemoteException); |
Short nextIndex() raises(SomeRemoteException); |
Short count() raises(SomeRemoteException); |
}; |
}; |
/* util/iterator/iterator.idl */ |
q两个接口ؓ下面跟具体业务有关的接口的实玎ͼ如ProductIterator接口的实现提供了基础?/TD> |
/* 具体业务有关的接口定义,本例中ؓProductIterator接口 */ |
#include “util/iterator/iterator.idl?/TD> |
module productcatalog |
{ |
struct productItem |
{ |
string productName; |
…?/TD> |
}; |
typedefsequence |
interface ProductIterator : iterator::BaseListIterator |
{ |
ProductItem next() raises (SomeRemoteException); |
ProductItemList nextBlock(in short size) raises (SomeRemoteException); |
ProductItem previous() raises (SomeRemoteException); |
ProductItemList previousBlock(in short size) raises (SomeRemoteException); |
}; |
interface ProductCatalog |
{ |
ProductIterator getProductItems(in string group,in string category,in string status) |
raises (SomeRemoteException); |
}; |
}; |
} |
值得注意的是Q在上面的代码中QProductCatalog接口中的getProductItemsҎq回的是ProductIterator而不是先前的ProductItemList; |
在服务器端,实现ProductIterator的类在管理数据传输的q程中vC集成的作用。而且从本质上来讲QProductIterator接口是个暂时的(transientQcorba对象Q在调用getProductItems()的过E中生成。之所以是暂时的,是因为它的内Ҏ查询的结果决定。如果查询后q回的结果是集合型的或者通过如ArrayList的集合类q回Q则ProductIterator的实现就比较单,因ؓArrayListcLw就已经提供了了ListIterator和Iterator。另外一个就是在服务器端E序实现ProductCatalog上。GetProductItemsҎq回ProductIterator对象的句柄,而不?Ҏ一中的Product IDs和方案二中的Products.׃Product和Product ID都是Corba的数据类型,因此服务器端的程序需要进行数据类型的映射和{换。而在Ҏ三中Q由于用了ProductIterator,数据cd的映就由服务器端的对象实现来决定了?/TD> |
在客LQ代理程序发起初始的hQ同时缓存了q端的P代(IteratorQ类的句柄。代理程序也可以在返回之前立dC面的N个数据。图五列Z客户端和服务器端lg的交互作用的q程?/TD> |
![]() |
当客L初始化一个请求时Q客L的代理程序首先检查缓冲中是否已经存在该请求的l果。如果缓冲中没有相关的信息,代理׃向服务器发出查询h。查询的l果是个q端的IteratorQ一个corba对象的句柄)Qƈ保存在客L的代理中。接下来的客Lh会索一个或一堆的产品信息。当Ӟ你也可以把品信息保存在~冲区。当初始化一个新的查询时Q你也可以先清除掉缓冲中的信息。下面的代码重新定义了实CProductServiceProxy接口?/TD> |
Public class ProductServiceProxy |
{ |
private int nItems=15; |
private ProductIterator remoteIterator; |
private static ProductServiceProxy instance; |
private ProductServiceProxy() {} |
public static ProductServiceClient getInstance() |
{ |
if(instance==null) |
instance=new ProductServiceProxy(); |
return instance; |
} |
public ArrayList getProducts(String group,String category,String |
status) throws SomeException |
{ |
ArrayList ProductList = new ArrayList(); |
Try |
{ |
ProductService productService= locateRemoteProductService(); |
If(productService==null) |
//return empty list or throw user defined exception |
try |
{ |
remoteIterator=productService.getProducts(?.); |
} |
catch(SystemException se){//handle exception} |
} |
catch(Exception e){//handle exception} |
return this.getMoreProducts(nItems); |
} |
public ArrayList getMoreProducts(int nItems) throws SomeException |
{ |
//retrive the next 10-15 items using cache iterator |
} |
?/TD> |
} |
׃q端的P代类QIteratorQ是个暂时的CORBA对象Q它要依赖于服务器端E序生命周期。因此,pȝ的设计一定要保证在客LE序完成之前Q该对象一定要存活?/TD> |
三.l论?/FONT> |
企业U应用程序的设计比较复杂。前期的设计对以后系l的性能会有很大的媄响。而在CORBA环境中,IDL的设计就昑־ؓ重要。好的IDL设计Q充分利用JDK的APIQ如计时器,垃圾回收机制Q集合框架能大大的提高系l的性能。从而能构造一个强壮的Q高度可用的分布式系l?/TD> |
在Q何项目中(亦或名字I间?都可能存在大量的 CORBA 对象Q如果所有这些对象需要有唯一的名字那么一定会有名字空间冲H。所?IDL 允许定义模块(module)Q模块指定一个独立的名字I间Q类g C++ 的名字空间的功能性。现在对于Q何接口都可以指定模块Q就象下面例子展C的那样Q?/FONT>
module FruitsBasket { interface Apple {}; interface Orange {}; }; |
在这里,我们?FruitsBasket 模块中定义了两个对象Q?Apple ?Orange。如果我们要从其他模块中引用一个对象,我们必须l出完全的对象引用,例如Q从 VegetablesBasket 模块中引?nbsp; Apple 要这样做QFruitsBasket::Apple?/FONT>
也可能重新打开同一个模块来增加接口定义Q下面的例子严格的等价于上一个?/FONT>
module FruitsBasket { interface Apple {}; }; module FruitsBasket { interface Orange {}; }; |
IDL?C++ 一样也有预~译宏指?directive): 支持 #include ?#pragma (q些宏指令由 idl-compiler 展开)?#include 同在 C/C++ 中有cM的语义。允怽Z清晰的目的而把在同一个模块中的不同的接口分隔在不同的文g中?/FONT>
/* q是一?C 式样的注?*/ // q是一?C++ 式样的注?: 两者都有效 // q些代码?apple.idl 文g? #include "orange.idl" module FruitsBasket interface Apple {}; }; |
// q些代码?orange.idl 文g? module FruitsBasket interface Orange {}; }; |
因ؓ IDL 的主要目的是实现在不同的语言、机器和操作pȝ之间的可UL性,他是强类型的。这里是最基本(标准)?CORBA cd:
?1-1. 基本 CORBA cd
cd | 意义 |
---|---|
short | 16 bit signed integer |
unsigned short | 16 bit unsigned integer |
long | 32 bit signed integer |
unsigned long | 32 bit unsigned integer |
long long | 64 bit signed integer |
unsigned long long | 64 bit unsigned integer |
float | 32 bit IEEE float |
double | 64 bit IEEE float |
long double | 128 bit float |
boolean | boolean value: TRUE or FALSE |
octet | 8 bit byte |
char | 8 bit character (ISO latin-1) |
wchar | 国际字符格式. FixMe: 谁知道这个格? |
string | Z char 的字W串 |
wstring | Z wchar 的字W串 |
它们的用是非常率直? q有一些用了字符串和点cd?IDL 接口声明:
module FruitsBasket { interface Apple { attribute string color; }; interface Orange { attribute float size; }; }; |
每个对象得到一个Q点类型或字符串类型的属性?/FONT>
有赖于const 修饰(p?C++ ? IDL 允许你定义常量。这里有一些样?IDL 代码:
module FruitsBasket { interface Apple { attribute string color; const float weight = 2.3; const string type = "sample type"; }; interface Orange { attribute float size; }; }; |
使用 typedef 关键字就可以定义你自qcd(q是很象?C ?C++ ?。又是一个样例代码,q里定义了一个有自定义的cd属性的 Apple 对象Q自定义的类型是一个字W串?/FONT>
module FruitsBasket { typedef string fruit_type; interface Apple { attribute fruit_type type; }; interface Orange { }; }; |
我们也有标准?C/C++ l构Q枚丑֒阵列。结构,枚D和定?fixed size)阵列?typedef 一L直就?C 代码Q?/FONT>
module calendar { enum a_month { january, february, march, april, may, june, july, august, september, october, december }; enum a_day { monday, tuesday, wednesday, thursday, friday, saturday, sunday }; typedef long a_year; struct a_date { a_day the_day; a_month the_month; a_year the_year; }; interface calendar { attribute a_date the_date; // 一l阵? typedef a_date a_date_array[20]; attribute a_date_array the_date_array; // 二维阵列 typedef a_date_array a_date_array_multi[20]; attribute a_date_array_multi the_date_array_multi; }; }; |
变长(Variable-size)阵列?CORBA 中叫序列(sequence):
module calendar { interface calendar { /* 定义了一个一l?long 阵列(数组) * 最大长度是 20. */ typedef sequence <long,20> array_1; // 一个无边界?unbounded)的一l字W串阵列 typedef sequence <string> array_2; // 更复杂的: 一个序列的序列 // 用于模拟多维变长阵列 typedef sequence <sequence <long,20> > array_3; }; }; |
所有这些标准的cd允许~程者定义对象的属性和Ҏ。对象方?函数)q样声明Q?/FONT>
module FruitsBasket { interface Apple { // eat_me Ҏ的声? // 有一?boolean 变量作ؓ参数 void eat_me (in boolean eat_yes_or_not ); // q回 Apple 是否被吃? boolean eaten (); // q回 Apple 是否被吃? // 和吃它的人的姓和? boolean who_ate ( out string who_surname, out string who_name ); }; }; |
除了 in 修饰Q这些声明非常象 C++ 代码: 参数?in, out, inout 三种cd之一来修饰。它们的语义如下Qin 参数是客户向对象发送的数据Qout 参数是对象向客户发送的数据Qinout 参数先从客户发送到对象Q再被返回给客户?/FONT>
所有这些方法声明都是同步操作,是_使用q些Ҏ意味着你的E序等到对象应{之后才l箋执行。可以定义异步的ҎQ这L话调用方法的E序可以l箋执行而不是在对象q回应答之前一直阻塞,异步Ҏ的声明用关键?oneway(单向)?/FONT>
module FruitsBasket { interface Apple { // eat_me Ҏ的声? // 有一?boolean 变量作ؓ参数 void eat_me (in boolean eat_yes_or_not ); // q回 Apple 是否被吃? // 异步Ҏ oneway boolean eaten (); // q回 Apple 是否被吃? // 和吃它的人的姓和? boolean who_ate ( out string who_surname, out string who_name ); }; }; |
也可以定义例?exception)(cM?C++ 的例外类)?/FONT>
module FruitBasket { exception no_more_fruits { string reason; }; interface Apple { // 我们所希望的各U的Ҏ }; }; |
最l,可以象下面这样声明有能力引发(throwing)例外的方法?/FONT>
module FruitsBasket { exception no_more_fruits { string reason; }; interface Apple { void eat_me (in boolean eat_yes_or_not ) raises ( no_more_fruits ); // 我们所希望的诸如此cȝҎ }; }; |
?C++ 的类(?Java 的接?Q接口可以从其他的接口承,q且支持多?multiple inheritance)。语法类g C++ 的语法。一个重要的不同?IDL 不支持重载或不同的方法可以有相同的名字和不同的参数说?signature)?/FONT>
module FruitsBasket { exception no_more_fruits { string reason; }; interface generic_fruit { void eat_me (in boolean eat_yes_or_not ) raises (no_more_fruits); oneway boolean eaten (); }; interface generic_fruit_one { boolean who_ate ( out string who_surname, out string who_name ); }; interface Apple : generic_fruit, generic_fruit_one { // q里是特定于 Apple 的方? }; }; |
我们展示了各U?IDL cdQ一些生成新的接口和属性、方法、例外的声明的方式。感兴趣的读者可以从 CORBA 标准中读到权威的 IDL 定义q且看到一些补充的cd?any、TypeCode、union。但q些cd对于本文不是很重要的?/FONT>