ICE的很多方法

来源:互联网 发布:网络机顶盒小米 编辑:程序博客网 时间:2024/04/28 10:14

本文介绍了ICE通信器、对象适配器、Servant定位器以及对象代理的更多使用方法

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”, Factory是category,File是name。

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. CommunicatorPtr initialize(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: LoggerPtr类型,这是一个日志记录器接口,它可以记录Ice运行过程中产生的跟踪、警告和错误信息,默认实现是直接向cerr输出。比如作用我们之前的Helloworld的例子,在没开服务端的情况下运行客户端,就看到在控制超台上打印了一串错误信息。

我们可以自己实现这个接口,以控制它的输出方向,它的定义为:

  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: StatsPtr类型,当Ice发送或接收到数据时,会向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:BasicStringConverter<wchar_t>类型;

这两个接口用于本地编码与UTF-8编码之间的转换,Ice系统自带了三套转换系统,默认的UnicodeWstringConverter、Linux/Unix 下使用的IconvStringConverter和Windows 下使用的WindowsStringConverter

threadHook: ThreadNotificationPtr类型,线程通知钩子,当Ice建立一个新线程后,线程通知钩子就会首先得到“线程启动通知”,在结束线程之前,也能得到“线程结束通知”。

下面是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. using namespace std;
  4. using namespace Demo;
  5. struct PrinterImp : Printer{
  6. virtual void printString(const ::std::string& s, const ::Ice::Current&)
  7. {
  8. cout << s << endl;
  9. }
  10. };
  11. class MyStats : public Ice::Stats {
  12. public:
  13. virtual void bytesSent(const string &prot, Ice::Int num)
  14. {
  15. cerr << prot << ": sent " << num << "bytes" << endl;
  16. }
  17. virtual void bytesReceived(const string &prot, Ice::Int num)
  18. {
  19. cerr << prot << ": received " << num << "bytes" << endl;
  20. }
  21. };
  22. class MyApp : public Ice::Application{
  23. public:
  24. virtual int run(int n, char* v[]){
  25. Ice::CommunicatorPtr& ic = communicator();
  26. ic->getProperties()->parseCommandLineOptions(
  27. "SimplePrinterAdapter", Ice::argsToStringSeq(n,v));
  28. Ice::ObjectAdapterPtr adapter
  29. = ic->createObjectAdapter("SimplePrinterAdapter");
  30. Ice::ObjectPtr object = new PrinterImp;
  31. adapter->add(object, ic->stringToIdentity("SimplePrinter"));
  32. adapter->activate();
  33. ic->waitForShutdown();
  34. return 0;
  35. }
  36. };
  37. int main(int argc, char* argv[])
  38. {
  39. MyApp app;
  40. Ice::InitializationData id;
  41. id.stats = new MyStats;
  42. return app.main(argc, argv, id);
  43. }

编译运行这个演示代码,然后执行客户端,可以看到打印出的接收到发送字符数。

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

 

 

 

对象适配器(Ice::ObjectAdapter)

对象适配器负责提供一个或多个传输端点,并且把进入的请求分派到对应的servant中去执行。它的定义以及主要方法有:

  1. namespace Ice
  2. {
  3. struct ObjectAdapter : public LocalObject
  4. {
  5. // 返回适配器的名字(由Communicator::createObjectAdapter输入)
  6. std::string getName() const;
  7. // 返回创建并拥有应适配器的通信器
  8. Ice::CommunicatorPtr getCommunicator() const;
  9. // 激活处于hold状态的适配器。
  10. void activate();
  11. // 要求适配器进入hold状态,并马上返回
  12. void hold();
  13. // 等待直到适配器进入hold状态
  14. void waitForHold();
  15. // 要求适配器进入无效状态,一旦进入无效状态,就无法再次激活它,与该适配器关联的servant将会被销毁。
  16. void deactivate();
  17. // 等待直到适配器进入无效状态
  18. void waitForDeactivate();
  19. // 如果适配器处于无效状态,返回true
  20. bool isDeactivated() const;
  21. // 使适配器处于无效状态,并且释放所有的资源(包括适配器名)
  22. void destroy();
  23. // 使用指定的标识把servant注册到适配器中,返回该servant的代理
  24. Ice::ObjectPrx add(const Ice::ObjectPtr&, const Ice::Identity&);
  25. // 使用随机生成的UUID作为标识把servant注册到适配器中,返回该servant的代理
  26. Ice::ObjectPrx addWithUUID(const Ice::ObjectPtr&);
  27. // 从适配器中移除对应的servant
  28. Ice::ObjectPtr remove(const Ice::Identity&);
  29. // 查找对应标识的servant
  30. Ice::ObjectPtr find(const Ice::Identity&) const;
  31. // 查找代理对应的servant
  32. Ice::ObjectPtr findByProxy(const Ice::ObjectPrx&) const;
  33. // 把一个 Servant Locator 添加到这个对象适配器中。
  34. void addServantLocator(const Ice::ServantLocatorPtr&, const std::string&);
  35. // 查找一个已经安装到这个对象适配器中的 Servant Locator。
  36. Ice::ServantLocatorPtr findServantLocator(const std::string&) const;
  37. // 创建一个与这个对象适配器及给定标识相匹配的代理
  38. Ice::ObjectPrx createProxy(const Ice::Identity&) const;
  39. // 创建一个与这个对象适配器及给定标识相匹配的 " 直接代理 "。
  40. // 直接代理总是包含有当前的适配器端点。
  41. Ice::ObjectPrx createDirectProxy(const Ice::Identity&) const;
  42. // 创建一个与这个对象适配器及给定标识相匹配的 " 间接代理 "。
  43. // 间接代理只包含对象的标识和适配器名,通过定位器服务来得到服务器地址
  44. Ice::ObjectPrx createIndirectProxy(const Ice::Identity&) const;
  45. // 为这个对象适配器设置一个 Ice 定位器
  46. void setLocator(const Ice::LocatorPrx&);
  47. // 重新读入适配器的PublicshedEndpoints属性并更新内部的可用网络接口列表
  48. void refreshPublishedEndpoints();
  49. };
  50. }

Servant定位器

除直接向对象适配器注册servant以外,Ice允许我们向对象适配器提供一个Servant定位器。

有了Servant定位器以后,对象适配器得到一次请求时首先查找已注册的servant,如果没找到对应的servant,就会请求 Servant定位器提供servant。

采用这种简单的机制,我们的服务器能够让我们访问数量不限的servant:服务器不必为每一个现有的Ice对象实例化一个单独的 servant。

下面是Servant定位器的接口,我们必须自己实现这个接口:

  1. namespace Ice
  2. {
  3. class ServantLocator : virtual public Ice::LocalObject
  4. {
  5. // 只要有请求到达,而且适配器没有提供注册的条目,Ice就会调用locate。
  6. // locate的实现(由你在派生类中提供)应该返回一个能够处理该请求的 servant。
  7. // 通过cookie参数,你可以传入一个自定义指针数据,对象适配器并不在乎这个对象的内容
  8. // 当Ice 调用finished 时,会把你从locate 返回的cookie传回给你。
  9. virtual Ice::ObjectPtr locate(const Ice::Current& curr,
  10. Ice::LocalObjectPtr& cookie) = 0;
  11. // 一旦请求完成,Ice就会调用finished。
  12. // 把完成了操作的servant、该请求的Current 对象
  13. // 以及locate在一开始创建的 cookie 传给它。
  14. virtual void finished(const Ice::Current& curr,
  15. const Ice::ObjectPtr& obj, const Ice::LocalObjectPtr& cookie) = 0;
  16. // 当servant定位器所属的对象适配器无效时,Ice会调用deactivate方法。
  17. virtual void deactivate(const std::string&) = 0;
  18. };
  19. }

实现了ServantLocator后,通过对象适配器的addServantLocator方法注册到该适配器中。

在上面的接口中,有一个Ice::Current结构类型的参数,通过它,我们就可以访问“正在执行的请求”和“服务器中的操作的实现”等信息,Ice::Current的定义如下:

  1. namespace Ice
  2. {
  3. struct Current
  4. {
  5. // 负责分派当前请求的对象适配器
  6. Ice::ObjectAdapterPtr adapter;
  7. //
  8. Ice::ConnectionPtr con;
  9. // 当前请求的对象标识
  10. Ice::Identity id;
  11. // 请求的 facet
  12. std::string facet;
  13. // 正在被调用的操作的名字。
  14. std::string operation;
  15. // 操作的调用模式(Normal、Idempotent,或Nonmutating)
  16. Ice::OperationMode mode;
  17. // 这个调用的当前上下文,这是一个std::map类型的数据
  18. // 使用它就允许把数量不限的参数从客户发往服务器
  19. Ice::Context ctx;
  20. //
  21. Ice::Int requestId;
  22. };
  23. }

利用Ice::Currentid成员,我们可以得到所请求的对象标识,从而决定生产某个具体的Servant;使用ctx成员,我们还可以从客户端发送数据不限的“键值-数值”对到服务器中,实现灵活控制(甚至连方法参数都可以不用了,全部用ctx转送就行)。

例:向原HelloWorld 版服务器加入Servant定位器代码,当请求的标识的category为"Loc"时,由定位器返回对应的 servant。

服务器端

  1. #include <ice/ice.h>
  2. #include "printer.h"
  3. using namespace std;
  4. using namespace Demo;
  5. // 原打印版本
  6. struct PrinterImp : Printer{
  7. virtual void printString(const ::std::string& s, const ::Ice::Current&)
  8. {
  9. cout << s << endl;
  10. }
  11. };
  12. // OutputDebugString版本
  13. struct DbgOutputImp : Printer{
  14. virtual void printString(const ::std::string& s, const ::Ice::Current&)
  15. {
  16. ::OutputDebugStringA(s.c_str());
  17. }
  18. };
  19. // MessageBox版本
  20. struct MsgboxImp : Printer{
  21. virtual void printString(const ::std::string& s, const ::Ice::Current&)
  22. {
  23. ::MessageBoxA(NULL,s.c_str(),NULL,MB_OK);
  24. }
  25. };
  26. struct MyLocator : Ice::ServantLocator{
  27. virtual Ice::ObjectPtr locate(const Ice::Current& curr,
  28. Ice::LocalObjectPtr& cookie)
  29. {
  30. if(curr.id.name == "Dbg")
  31. return Ice::ObjectPtr(new DbgOutputImp);
  32. else if(curr.id.name == "Msg")
  33. return Ice::ObjectPtr(new MsgboxImp);
  34. else if(curr.id.name == "SimplePrinter")
  35. return Ice::ObjectPtr(new PrinterImp);
  36. else
  37. return NULL;
  38. }
  39. virtual void finished(const Ice::Current& curr,
  40. const Ice::ObjectPtr& obj, const Ice::LocalObjectPtr&)
  41. {
  42. }
  43. virtual void deactivate(const std::string& category)
  44. {
  45. }
  46. };
  47. class MyApp : public Ice::Application{
  48. public:
  49. virtual int run(int n, char* v[]){
  50. Ice::CommunicatorPtr& ic = communicator();
  51. ic->getProperties()->parseCommandLineOptions(
  52. "SimplePrinterAdapter", Ice::argsToStringSeq(n,v));
  53. Ice::ObjectAdapterPtr adapter
  54. = ic->createObjectAdapter("SimplePrinterAdapter");
  55. Ice::ObjectPtr object = new PrinterImp;
  56. adapter->add(object, ic->stringToIdentity("SimplePrinter"));
  57. // 注册MyLocator定位器,负责返回category为"Loc"的相应servant。
  58. adapter->addServantLocator(Ice::ServantLocatorPtr(new MyLocator),"Loc");
  59. adapter->activate();
  60. ic->waitForShutdown();
  61. return 0;
  62. }
  63. };
  64. int main(int argc, char* argv[])
  65. {
  66. MyApp app;
  67. return app.main(argc, argv);
  68. }

客户端代码与《ICE属性配置》中的代码相同,客户端命令行参数为

--MyProp.Printer="Loc/Msg:tcp -p 10000"

服务器端将会使用对话框显示“HelloWorld”。


 

对象代理(Object Proxy)

在客户端,我们使用对象代理进行远程调用,就如它们就在本地一样。但有时,网络问题还是要考虑的,于是Ice的对象代理提供了几个包装方法,以支持一些网络特性:

ice_timeout方法,声明为:Ice::ObjectPrx ice_timeout(int) const;返回一个超时代理,当在指定的时间(单位毫秒)内没有得到服务器端响应时,操作终止并抛出Ice::TimeoutException异常。

示例代码
  1. Filesystem::FilePrx myFile = ...;
  2. FileSystem::FilePrx timeoutFile
  3. = FileSystem::FilePrx::uncheckedCast(
  4. myFile->ice_timeout(5000));
  5. try {
  6. Lines text = timeoutFile->read(); // Read with timeout
  7. } catch(const Ice::TimeoutException &) {
  8. cerr << "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. // Down-cast to actual type.
  5. PersonPrx onewayPerson = PersonPrx::uncheckedCast(oneway);
  6. // Invoke an operation as oneway.
  7. try {
  8. onewayPerson->someOp();
  9. } catch (const Ice::TwowayOnlyException &) {
  10. cerr << "someOp() is not oneway" << endl;
  11. }

ice_datagram方法,声明为:Ice::ObjectPrx ice_datagram() const;返回数据报代理,它使用UDP传输机制,并且和单向调用代理一样,不会得到服务器端的答复,而且还有可能UDP包重复和不按次序到达服务端。

示例代码
  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. cerr << "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. cerr << "someOp() is not oneway" << endl;
  19. }

批量调用代理
Ice::ObjectPrx ice_batchOneway() const; Ice::ObjectPrx ice_batchDatagram() const; void ice_flushBatchRequests(); 

为了提供网络效率,对于单向调用,可以考虑把多个调用打包一起送往服务器,Ice对象代理提供了ice_batchOnewayice_batchDatagram方法返回对应的批调用代理,使用这种代理时呼叫信息不会马上发出,而是等到调用ice_flushBatchRequests以后才一次性发出。

示例代码
  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();

 

 

 

 

原创粉丝点击