开发者社区> 问答> 正文

Ice的更多细节 - ICE报错

"

本文介绍了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(intchar*[]);
  6.         int main(intchar*[], const char*);
  7.         int main(intchar*[], 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.  
  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.         cerr << prot << ": sent " << num << "bytes" << endl;
  19.     }
  20.     virtual void bytesReceived(const string &prot, Ice::Int num)
  21.     {
  22.         cerr << prot << ": 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.             = ic->createObjectAdapter("SimplePrinterAdapter");
  34.         Ice::ObjectPtr object = new PrinterImp;
  35.         adapter->add(object, ic->stringToIdentity("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.     id.stats = 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

对象适配器(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属性并更"

展开
收起
montos 2020-06-03 21:55:58 1101 0
1 条回答
写回答
取消 提交回答
  • "好文章,慢慢研究。######

    好!!!顶一个、、、

    话说看书看了好多章,没楼主总结的经典、、、


    "
    2020-06-03 22:31:38
    赞同 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
Intel® Data Direct I/O(Intel®D 立即下载
低代码开发师(初级)实战教程 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载