ICE的整体架构
服务器端:
服务器端通常只有一个通信器(Ice::Communicator),通信器包含了一系列的资源:
如线程池、配置属性、对象工厂、日志记录、统计对象、路由器、定位器、插件管理器、对象适配器
在通信器内,包含有一个或更多的对象适配器(Ice::ObjectAdapter),对象适配器负责提供一个或多个传输端点,并且把进入的请求分派到对应的servant中去执行。
具体实现的部分称为servant,它们为客户端发来的调用提供服务。servant向对象适配器注册以后,由对象适配器依据客户请求调用相应方法。
客户端:
客户端直接通过代理进行远程调用,就象本地调用一样简单。
通信器Ice::Communicator
通信器管理着线程池、配置属性、对象工厂、日志记录、统计对象、路由器、定位器、插件管理器、对象适配器。
通信器的几个重要方法:
std::string proxyToString (const Ice::ObjectPrx&) const;
Ice::ObjectPrx stringToProxy (const std::string&) const;
这两个方法可以使代理对象和字符串之间互相转换。对于proxyToString方法,你也可以使用代理对象的 ice_toString方法代替(当然,你要确保是非空的代替对象)。
Ice::ObjectPrx propertyToProxy (const std::string&) const;
这个方法根据给定名字的属性配置生成一个代理对象,如果没有对应属性,返回一个空代理。
比如有如下属性:
MyApp.Proxy = ident:tcp -p 5000
我们就可以这样得到它的代理对象:
Ice::ObjectPrx p = communicator->propertyToProxy("MyApp.Proxy");
Ice::Identity stringToIdentity (const std::string&) const;
std::string identityToString (const Ice::Identity&) const;
转换字符串到一个对象标识,对象标识的定义如下:
1. namespace Ice
2. {
3. struct Identity
4. {
5. std::string name;
6. std::string category;
7. };
8. }
CATEGORY/NAME 。比如字符串“Factory/File
category部分可以为空。
Ice::ObjectAdapterPtr createObjectAdapter (const std::string&);
Ice::ObjectAdapterPtr createObjectAdapterWithEndpoints (
const std::string&, const std::string&);
这两个方法创建新的对象适配器。createObjectAdapter从属性配置中取得端点信息,而 createObjectAdapterWithEndpoints则直接指定端点。
void shutdown ();
关闭服务端的Ice运行时库,调用shutdown后,执行过程中的操作仍可正常完成,shutdown不会等待这些操作完成。
void waitForShutdown ();
这个方法会挂起发出调用的线程直到通信器关闭为止。
void destroy ();
这个方法回收通信器的相关资源,如线程、通信端点及内存资源。在离开main函数之前,必须调用destory。
bool isShutdown () const;
如果shutdown已被调用过,则返回true。
初始化通信器
在建立通信器(Ice::Communicator)期间,Ice运行时会初始化一系列的对象,这些对象一直影响通信器的整个生命周期。并且在建立通信器以后,你不能改变这些对象。所以,如果你想定制这些对象,就必须在建立通信器的过程中定义。
在通信器建立期间,我们可以定义下面这些对象:
- 属性表(property)
- 日志记录器(Logger)
- 统计对象(Stats)
- 原生字符串与宽字符串转换器
- 线程通知钩子
InitializationData
1. namespace Ice {
2. struct InitializationData {
3. PropertiesPtr properties;
4. LoggerPtr logger;
5. StatsPtr stats;
6. StringConverterPtr stringConverter;
7. WstringConverterPtr wstringConverter;
8. ThreadNotificationPtr threadHook;
9. };
10. }
这个结构中的所有成员都是智能指针类型,设置好这些成员以后,就可以通过通信器的初始化函数传入这些对象:
1. namespace Ice {
2. int &, char *[],
3. const InitializationData& = InitializationData());
4. CommunicatorPtr initialize(StringSeq&,
5. const InitializationData& = InitializationData());
6. CommunicatorPtr initialize(
7. const InitializationData& = InitializationData());
8. }
我们前面使用的Ice::Application也提供了InitializationData的传入途径:
1. namespace Ice
2. {
3. struct Application
4. {
5. int main( int , char *[]);
6. int main( int , char *[], const char *);
7. int main( int , char *[], const Ice::InitializationData&);
8. int main( const StringSeq&);
9. int main( const StringSeq&, const char *);
10. int main( const StringSeq&, const Ice::InitializationData&);
11. ...
12. };
13. }
再回头看InitializationData结构:
properties :PropertiesPtr 类型,指定了属性表(property)对象,它就是之前《Ice属性配置 》一文中的主角。默认的属性表实现可以解析“Key = Value”这种形式的字符串(包括命令行参数和文件),如果愿意,你可以自己写一个属性表实现,用来解析xml、ini等等。
如果要自己实现,就得完成下面这些接口(每个方法的作用请参考《Ice属性配置 》):
1. namespace Ice
2. {
3. class Properties : virtual public Ice::LocalObject
4. {
5. public :
6. virtual std::string getProperty( const std::string&) = 0;
7. virtual std::string getPropertyWithDefault( const std::string&,
8. const std::string&) = 0;
9. virtual Ice::Int getPropertyAsInt( const std::string&) = 0;
10. virtual Ice::Int getPropertyAsIntWithDefault( const std::string&,
11. Ice::Int) = 0;
12. virtual Ice::StringSeq getPropertyAsList( const std::string&) = 0;
13. virtual Ice::StringSeq getPropertyAsListWithDefault( const std::string&,
14. const Ice::StringSeq&) = 0;
15. virtual Ice::PropertyDict getPropertiesForPrefix( const std::string&) = 0;
16. virtual void setProperty( const std::string&, const std::string&) = 0;
17. virtual Ice::StringSeq getCommandLineOptions() = 0;
18. virtual Ice::StringSeq parseCommandLineOptions( const std::string&,
19. const Ice::StringSeq&) = 0;
20. virtual Ice::StringSeq parseIceCommandLineOptions( const Ice::StringSeq&) = 0;
21. virtual void load( const std::string&) = 0;
22. virtual Ice::PropertiesPtr clone() = 0;
23. };
24. };
logger
我们可以自己实现这个接口,以控制它的输出方向,它的定义为:
1. namespace Ice
2. {
3. class Logger : virtual public Ice::LocalObject
4. {
5. public :
6. virtual void print( const std::string& msg) = 0;
7. virtual void trace( const std::string& category,
8. const std::string& msg) = 0;
9. virtual void warning( const std::string& msg) = 0;
10. virtual void error( const std::string& msg) = 0;
11. };
12. }
不用说,实现它们是一件很轻松的事情^_^,比如你可以实现这个接口把信息写到一个日志文件里,或者把它写到某个日志服务器上。
stats
1. namespace Ice
2. {
3. class Stats : virtual public Ice::LocalObject
4. {
5. public :
6. virtual void bytesSent( const std::string& protocol,
7. Ice::Int num) = 0;
8. virtual void bytesReceived( const std::string& protocol,
9. Ice::Int num) = 0;
10. };
11. }
stringConverter :BasicStringConverter<char>类型;
wstringConverter
UnicodeWstringConverter 、Linux/Unix 下使用的IconvStringConverter 和Windows 下使用的WindowsStringConverter
threadHook
下面是ThreadNotification接口的定义:
1. namespace Ice
2. {
3. class ThreadNotification : public IceUtil::Shared {
4. public :
5. virtual void start() = 0;
6. virtual void stop() = 0;
7. };
8. }
假如我们在Windows下使用了COM组件的话,就可以使用线程通知钩子在start和stop里调用 CoInitializeEx和CoUninitialize。
代码演示
修改一下Helloworld 服务器端代码,实现自定义统计对象(Stats
1. #include <ice/ice.h>
2. #include "printer.h"
3.
4. using namespace std;
5. using namespace Demo;
6.
7. struct PrinterImp : Printer{
8. virtual void printString( const ::std::string& s, const ::Ice::Current&)
9. {
10. cout << s << endl;
11. }
12. };
13.
14. class MyStats : public Ice::Stats {
15. public :
16. virtual void bytesSent( const string &prot, Ice::Int num)
17. {
18. ": sent " << num << "bytes" << endl;
19. }
20. virtual void bytesReceived( const string &prot, Ice::Int num)
21. {
22. ": received " << num << "bytes" << endl;
23. }
24. };
25.
26. class MyApp : public Ice::Application{
27. public :
28. virtual int run( int n, char * v[]){
29. Ice::CommunicatorPtr& ic = communicator();
30. ic->getProperties()->parseCommandLineOptions(
31. "SimplePrinterAdapter" , Ice::argsToStringSeq(n,v));
32. Ice::ObjectAdapterPtr adapter
33. "SimplePrinterAdapter" );
34. new PrinterImp;
35. "SimplePrinter" ));
36.
37. adapter->activate();
38. ic->waitForShutdown();
39. return 0;
40. }
41. };
42.
43. int main( int argc, char * argv[])
44. {
45. MyApp app;
46. Ice::InitializationData id;
47. new MyStats;
48.
49. return app.main(argc, argv, id);
50. }
编译运行这个演示代码,然后执行客户端,可以看到打印出的接收到发送字符数。
tcp: send 14bytes
tcp: received 14bytes
tcp: received 52bytes
tcp: send 26bytes
tcp: received 14bytes
tcp: received 53bytes
Hello World!
tcp: send 25bytes
tcp: received 14bytes
对象代理(Object Proxy)
在客户端,我们使用对象代理进行远程调用,就如它们就在本地一样。但有时,网络问题还是要考虑的,于是Ice的对象代理提供了几个包装方法,以支持一些网络特性:
ice_timeout方法 ,声明为:Ice::ObjectPrx ice_timeout(int) const;
示例代码
1. Filesystem::FilePrx myFile = ...;
2. FileSystem::FilePrx timeoutFile
3. = FileSystem::FilePrx::uncheckedCast(
4. myFile->ice_timeout(5000));
5. try {
6. // Read with timeout
7. } catch ( const Ice::TimeoutException &) {
8. "invocation timed out" << endl;
9. }
10. Lines text = myFile->read(); // Read without timeout
ice_oneway方法 ,声明为:Ice::ObjectPrx ice_oneway() const;
示例代码
1. Ice::ObjectPrxo=communicator->stringToProxy( /* ... */ );
2. // Get a oneway proxy.
3. Ice::ObjectPrx oneway = o->ice_oneway();
4.
5. // Down-cast to actual type.
6. PersonPrx onewayPerson = PersonPrx::uncheckedCast(oneway);
7. // Invoke an operation as oneway.
8. try {
9. onewayPerson->someOp();
10. } catch ( const Ice::TwowayOnlyException &) {
11. "someOp() is not oneway" << endl;
12. }
ice_datagram方法 ,声明为:Ice::ObjectPrx ice_datagram() const;
示例代码
1. Ice::ObjectPrxo=communicator->stringToProxy( /* ... */ );
2. // Get a datagram proxy.
3. //
4. Ice::ObjectPrx datagram;
5. try {
6. datagram = o->ice_datagram();
7. } catch ( const Ice::NoEndPointException &) {
8. "No endpoint for datagram invocations" << endl;
9. }
10. // Down-cast to actual type.
11. //
12. PersonPrx datagramPerson = PersonPrx::uncheckedCast(datagram);
13. // Invoke an operation as a datagram.
14. //
15. try {
16. datagramPerson->someOp();
17. } catch ( const Ice::TwowayOnlyException &) {
18. "someOp() is not oneway" << endl;
19. }
批量调用代理 :
Ice::ObjectPrx ice_batchOneway() const; Ice::ObjectPrx ice_batchDatagram() const; void ice_flushBatchRequests();
为了提供网络效率,对于单向调用,可以考虑把多个调用打包一起送往服务器,Ice对象代理提供了ice_batchOneway 和ice_batchDatagram
示例代码
1. Ice::ObjectPrx base = ic->stringToProxy(s);
2. PrinterPrx printer = PrinterPrx::uncheckedCast(base->ice_batchOneway());
3. if (!printer) throw "Invalid Proxy!" ;
4. printer->printString("Hello" );
5. printer->printString("World" );
6. printer->ice_flushBatchRequests();