1. 知名对象概述

有两种类型的间接代理:一种指定ID和对象适配器标识符,而另一个仅包含ID。后一种间接代理被称为知名代理。一个知名代理是指一个知名对象,也就是说,它的ID就足以让客户端找到它。Ice要求应用程序中的所有对象ID都是唯一的,但通常,只有少数几个对象能通过它们的ID定位。

在前面的部分中,我们展示了包含对象适配器标识符的间接代理和IceGrid配置之间的关系。简而言之,为了使客户端使用诸如factory@EncoderAdapter之类的代理,就必须为对象适配器分配标识符EncoderAdapter

对于知名对象也有类似的要求。注册表维护这些对象的表格,可以通过多种方式填充这些表格:

  • 在描述符中静态定义
  • 以编程方式使用IceGrid的管理界面配置
  • 动态使用IceGrid管理工具

注册表的数据库将对象的ID映射到代理。仅包含ID的定位请求,会提示注册表查阅此数据库。如果找到匹配项,注册表会检查相关的代理来确定是否需要额外的工作。例如,思考请下表中的知名对象。

ID 代理
Object1 Object1:tcp -p 10001
Object2 Object2@TheAdapter
Object3 Object3

Object1关联的代理已经包含端点,因此注册表可以简单地将此代理返回给客户端。

对于Object2,注册表通知适配器ID,并检查是否知道ID为TheAdapter的适配器。如果知道,它将尝试获取该适配器的端点,这可能会导致它的服务端启动。如果注册表能够成功的确定适配器的端点,则会将包含这些端点的直接代理返回给客户端。如果注册表不能识别TheAdapter或无法获取它的端点,则会将间接代理Object2@TheAdapter返回给客户端。在收到另一个间接代理时,客户端的Ice运行时将再次尝试解析代理,但通常这将不会成功,因此客户端的Ice运行时将引发NoEndpointException

最后,Object3代表了一种绝望的情况:Object3如何从注册表中解析,当它关联的代理指向它自己?在这种情况下,注册表将代理Object3返回给客户端,从而导致客户端引发NoEndpointException。显然,你应该避免这种情况。

2. 知名对象类型

注册表的数据库不仅将ID与代理相关联起来,而且也是一个类型。从技术上讲,“类型”是一个任意的字符串,但是按照惯例,该字符串表示对象最底层的Slice类型。例如,我们Ripper中的编码器对象的Slice类型ID是::Ripper::MP3EncoderFactory

对象类型在执行查询时很有用。

3. 部署知名对象

对象描述符将一个知名对象添加到注册表中。它必须出现在适配器描述符的上下文中,如下面的XML示例所示:

<icegrid>
    <application name="Ripper">
        <node name="Node1">
            <server id="EncoderServer" exe="/opt/ripper/bin/server" activation="on-demand">
                <adapter name="EncoderAdapter" id="EncoderAdapter" endpoints="tcp">
                    <object identity="EncoderFactory" type="::Ripper::MP3EncoderFactory"/>
                </adapter>
            </server>
        </node>
    </application>
</icegrid>

在部署期间,注册表将IDEncoderFactory与间接代理EncoderFactory@EncoderAdapter相关联。如果适配器描述符省略了适配器ID,则注册表将结合服务器ID和适配器名称来生成唯一标识符。

在这个例子中,对象的类型是明确指定的。

4. 以编码的方式添加知名对象

IceGrid::Admin接口定义了几个方法来操作知名对象注册表的数据库:

module IceGrid 
{
    interface Admin
    {
        ...
        void addObject(Object* obj)
            throws ObjectExistsException,
               DeploymentException;
        void updateObject(Object* obj)
            throws ObjectNotRegisteredException,
               DeploymentException;
        void addObjectWithType(Object* obj, string type)
            throws ObjectExistsException,
               DeploymentException;
        void removeObject(Ice::Identity id) 
            throws ObjectNotRegisteredException,
                   DeploymentException;
    ...
    }
}
  • addObject

addObject添加新的对象到数据库。代理参数提供了知名对象的ID。如果具有相同ID的对象已被注册,则操作会抛出ObjectExistsException异常。由于此方法不接受给定对象类型的参数,因此注册表将调用给定代理上的ice_id以确定最底层的类型。这里的含义是对象必须是可用的,以便注册表获得它的类型。如果对象不可用,addObject抛出DeploymentException异常。

  • updateObject

updateObject为ID已经封装在代理中的知名对象添加一个新的代理。如果给定的ID没有对应注册过的对象,则操作会抛出ObjectNotRegisteredException。该方法不会修改对象的类型。

  • addObjectWithType

addObjectWithType方法的行为与addObject类似,除了对象的类型是显式指定的,因此注册表不会尝试调用给定代理上的ice_id(即使类型为空字符串)。

  • removeObject

removeObject从数据库中删除给定ID的知名对象。如果给定的ID没有对应注册过的对象,则操作会抛出ObjectNotRegisteredException异常。

以下C++示例产生的结果,与我们之前部署的描述符相同:

//C++11
auto adapter = communicator->createObjectAdapter("EncoderAdapter");
auto ident = Ice::stringToIdentity("EncoderFactory");
auto f = make_shared<FactoryI>();
auto factory = adapter->add(f, ident);
std::shared_ptr<IceGrid::AdminPrx> admin = // ...
try 
{
    admin->addObject(factory); // OOPS!
}
catch(const IceGrid::ObjectExistsException&) 
{
    admin->updateObject(factory);
}
//C++98
Ice::ObjectAdapterPtr adapter = communicator->createObjectAdapter("EncoderAdapter");
Ice::Identity ident = Ice::stringToIdentity("EncoderFactory");
FactoryPtr f = new FactoryI;
Ice::ObjectPrx factory = adapter->add(f, ident);
IceGrid::AdminPrx admin = // ...
try 
{
    admin->addObject(factory); // OOPS!
}
catch(const IceGrid::ObjectExistsException&) 
{
    admin->updateObject(factory);
}

在获得IceGrid::Admin接口的代理之后,代码将调用addObject。 注意,代码捕获ObjectExistsException异常,并当对象已经注册时调用updateObject来替代addObject

在这个代码中有一个小问题:调用addObject会导致注册表在我们的工厂对象上调用ice_id,此时,我们还没有激活对象适配器。结果,我们的程序会永久的挂在addObject的调用上。 一种解决方案是在调用addObject之前激活适配器;另一个解决方案是使用addObjectWithType,如下所示:

//C++11
auto adapter = communicator->createObjectAdapter("EncoderAdapter");
auto ident = Ice::stringToIdentity("EncoderFactory");
auto f = std::make_shared<FactoryI>();
auto factory = adapter->add(f, ident);
std::shared_ptr<IceGrid::AdminPrx> admin = // ...
try
{
    admin->addObjectWithType(factory, factory->ice_id());
} 
catch(const IceGrid::ObjectExistsException&)
{
    admin->updateObject(factory);
}
//C++98
Ice::ObjectAdapterPtr adapter = communicator->createObjectAdapter("EncoderAdapter");
Ice::Identity ident = Ice::stringToIdentity("EncoderFactory");
FactoryPtr f = new FactoryI;
Ice::ObjectPrx factory = adapter->add(f, ident);
IceGrid::AdminPrx admin = // ...
try
{
    admin->addObjectWithType(factory, factory->ice_id());
} 
catch(const IceGrid::ObjectExistsException&)
{
    admin->updateObject(factory);
}

5. 通过icegridadmin增加知名对象

icegridadmin工具提供的命令与Slice方法管理知名对象具有相同的功能。我们可以使用该工具,从我们的描述符中,手动注册EncoderFactory对象:

$ icegridadmin --Ice.Config=/opt/ripper/config
>>> object add "EncoderFactory@EncoderAdapter"

使用object list命令验证对象是否已成功注册:

>>> object list
EncoderFactory
IceGrid/Query
IceGrid/Locator
IceGrid/Registry
IceGrid/InternalRegistry-Master

如果要明确指定对象的类型,请将它追加到对象add命令:

>>> object add "EncoderFactory@EncoderAdapter" "::Ripper::MP3EncoderFactory"

最后,像这样从注册表中移除对象:

>>> object remove "EncoderFactory"

6. 查询知名对象

知名对象的注册表数据库不仅仅用于解析间接代理,数据库也提供多种交互方式来查询对象。IceGrid::Query接口提供了这个功能:

module IceGrid 
{
    enum LoadSample
    {
        LoadSample1,
        LoadSample5,
        LoadSample15
    }

    interface Query
    {
        idempotent Object* findObjectById(Ice::Identity id);
        idempotent Object* findObjectByType(string type);
        idempotent Object* findObjectByTypeOnLeastLoadedNode(string type, LoadSample sample);
        idempotent Ice::ObjectProxySeq findAllObjectsByType(string type);
        idempotent Ice::ObjectProxySeq findAllReplicas(Object* proxy);
    }
}
  • findObjectById

findObjectById返回与知名对象给定ID关联的代理。如果找不到匹配,返回一个空的代理。

  • findObjectByType

findObjectByType返回用给定类型注册对象的代理。如果多个对象有相同的类型,则注册表将随机选择一个。如果找不到匹配项,返回空代理。

  • findObjectByTypeOnLeastLoadedNode

findObjectByTypeOnLeastLoadedNode在考虑系统负载的情况下,选择一个给定类型的对象。如果注册表无法确定哪个节点托管这个对象(例如,该对象是使用直接代理而不是适配器ID进行注册的),则为了执行此操作,该对象被认为具有1的加载值。示例参数确定负载平均的时间间隔(1,5或15分钟)。如果找不到匹配项,返回空代理。

  • findAllObjectsByType

findAllObjectsByType以序列的方式返回具有相同给定类型知名对象的代理。如果找不到匹配项,返回一个空序列。

  • findAllReplicas

给定复制对象的间接代理,findAllReplicas以序列的方式返回单个副本的代理。应用程序可以使用这个方法,当需要直接与一个或多个副本进行通信时。

请注意,这些方法接受类型参数的操作,不等同于在每个对象上调用ice_isA去确定它是否支持给定的类型,这种技术对于大量注册对象来说,不能很好地扩展。相反,这些方法只是简单的将给定类型与对象的注册类型进行比较,或者如果对象注册时没有类型,则通过注册表用对象最底层Slice类型来确定。

从Ice3.7开始,按类型查找函数,只会从启用的服务器或未通过部署描述符注册的代理,来返回知名对象的代理。

7. 在Ripper中使用知名对象

知名对象是我们可以加入到Ripper的另一个IceGrid功能。

7.1. 给Ripper增加知名对象

首先,我们向描述符中增加2个知名对象:

<icegrid>
    <application name="Ripper">
        <node name="Node1">
            <server id="EncoderServer1" exe="/opt/ripper/bin/server" activation="on-demand">
                <adapter name="EncoderAdapter" endpoints="tcp">
                    <object identity="EncoderFactory1" type="::Ripper::MP3EncoderFactory"/>
                </adapter>
            </server>
        </node>
        <node name="Node2">
            <server id="EncoderServer2" exe="/opt/ripper/bin/server" activation="on-demand">
                <adapter name="EncoderAdapter" endpoints="tcp">
                    <object identity="EncoderFactory2" type="::Ripper::MP3EncoderFactory"/>
                </adapter>
            </server>
        </node>
    </application>
</icegrid>

乍一看,知名对象的增加似乎并不能简化我们的客户端。我们现在需要选择一个知名对象,而不是选择哪个适配器接收下一个任务。

7.2. 使用findAllObjectsByType查询Ripper对象

IceGrid::Query接口提供了一种消除客户端对对象适配器标识符和对象ID依赖的方法。由于我们的工厂对象是以相同的类型注册的,因此,我们可以搜索这种类型的所有对象:

//C++11
auto proxy = communicator->stringToProxy("IceGrid/Query");
auto query = Ice::checkedCast<IceGrid::QueryPrx>(proxy);
string type = Ripper::MP3EncoderFactory::ice_staticId();
auto seq = query->findAllObjectsByType(type);
if(seq.empty())
{
    // 没有匹配到
}
Ice::ObjectProxySeq::size_type index = ... // 随机数
auto factory = Ice::checkedCast<Ripper::MP3EncoderFactoryPrx>(seq[index]);
auto encoder = factory->createEncoder();
//C++98
Ice::ObjectPrx proxy = communicator->stringToProxy("IceGrid/Query");
IceGrid::QueryPrx query = IceGrid::QueryPrx::checkedCast(proxy);
string type = Ripper::MP3EncoderFactory::ice_staticId();
Ice::ObjectProxySeq seq = query->findAllObjectsByType(type);
if(seq.empty())
{
    // 没有匹配到
}
Ice::ObjectProxySeq::size_type index = ... // 随机数
Ripper::MP3EncoderFactoryPrx factory = Ripper::MP3EncoderFactoryPrx::checkedCast(seq[index]);
Ripper::MP3EncoderPrx encoder = factory->createEncoder();

这个例子调用findAllObjectsByType,然后随机选择一个序列的元素。

7.3. 使用findObjectByType查询Ripper对象

我们可以使用findObjectByType来进一步简化客户端,它为我们执行随机化:

//C++11
auto proxy = communicator->stringToProxy("IceGrid/Query");
auto query = Ice::checkedCast<Grid::QueryPrx>(proxy);
string type = Ripper::MP3EncoderFactory::ice_staticId();
auto obj = query->findObjectByType(type);
if(!obj) 
{
    // 没有匹配到
}
auto factory = Ice::checkedCast<Ripper::MP3EncoderFactoryPrx>(obj);
auto encoder = factory->createEncoder();
//C++98
Ice::ObjectPrx proxy = communicator->stringToProxy("IceGrid/Query");
IceGrid::QueryPrx query = IceGrid::QueryPrx::checkedCast(proxy);
string type = Ripper::MP3EncoderFactory::ice_staticId();
Ice::ObjectPrx obj = query->findObjectByType(type);
if(!obj) 
{
    // 没有匹配到
}
Ripper::MP3EncoderFactoryPrx factory = Ripper::MP3EncoderFactoryPrx::checkedCast(obj);
Ripper::MP3EncoderPrx encoder = factory->createEncoder();

7.4. 使用findObjectByTypeOnLeastLoadedNode查询Ripper对象

到目前为止,IceGrid::Query的使用使我们能够简化客户端,但是我们还没有得到任何功能。如果我们调用findObjectByTypeOnLeastLoadedNode来替换findObjectByType,我们可以通过更加智能地分配编码任务来改善客户端。只需要对客户的代码进行一小点变化:

//C++11
auto proxy = communicator->stringToProxy("IceGrid/Query");
auto query = Ice::checkedCast<IceGrid::QueryPrx>(proxy);
string type = Ripper::MP3EncoderFactory::ice_staticId();
auto obj = query->findObjectByTypeOnLeastLoadedNode(type, IceGrid::LoadSample1);
if(!obj)
{
    // 没有匹配到
}
auto factory = Ice::checkedCast<Ripper::MP3EncoderFactoryPrx>(obj);
auto encoder = factory->createEncoder();
//C++98
Ice::ObjectPrx proxy = communicator->stringToProxy("IceGrid/Query");
IceGrid::QueryPrx query = IceGrid::QueryPrx::checkedCast(proxy);
string type = Ripper::MP3EncoderFactory::ice_staticId();
Ice::ObjectPrx obj = query->findObjectByTypeOnLeastLoadedNode(type, IceGrid::LoadSample1);
if(!obj)
{
    // 没有匹配到
}
Ripper::MP3EncoderFactoryPrx factory = Ripper::MP3EncoderFactoryPrx::checkedCast(obj);
Ripper::MP3EncoderPrx encoder = factory->createEncoder();

7.5. Ripper进度回顾

智能负载分配是一个值得添加的功能,如果靠我们自己来实现,这也是一个耗时的功能。但是,我们目前的设计,只使用知名的对象来使查询成为可能。实际上,我们并不需要每个计算服务器上的编码器工厂对象作为知名对象单独寻址,当我们检查分配给它们的ID时,这个过程看起来很清晰:EncoderFactory1EncoderFactory2等等。IceGrid的复制功能为我们提供了改进设计所需的工具。

Copyright © github.com/weiofcn 2017 all right reserved,powered by GitbookLast modified time: 2017-12-21 13:30:06

results matching ""

    No results matching ""