本节将介绍一个示例应用程序,来演示IceGrid的功能。

1. Ripper应用示例

我们的应用程序从光盘(CD)上读取音乐曲目,并将它们编码为MP3文件,如下所示:

Overview of sample application
Figure: Overview of sample application

翻录整张CD通常需要几分钟的时间,因为MP3编码需要大量的CPU周期。我们的分布式应用程序Ripper利用远程Ice服务器上强大的CPU加速了这个过程,使我们能够并行处理多首歌曲。

MP3编码器的Slice接口非常简单:

#include <Ice/BuiltinSequences.ice>
module Ripper
{
    exception EncodingFailedException 
    {
        string reason;
    }

    sequence<short> Samples;

    interface Mp3Encoder
    {
        // Input: 左和右声道的PCM采样。
        // Output: MP3帧.
        Ice::ByteSeq encode(Samples leftSamples, Samples rightSamples)
            throws EncodingFailedException;

        // 通过flush来获取最后一帧,同时,flush以后也会销毁编码对象。
        Ice::ByteSeq flush() throws EncodingFailedException;
    }

    interface Mp3EncoderFactory
    {
        Mp3Encoder* createEncoder();
    }
}

编码算法的实现不在这里讨论。在这里,随着我们讨论IceGrid的特性,我们将逐步改进Ripper。

2. 初始Ripper架构

我们故意把应用程序的初始架构设计的很简单,一个IceGrid注册表和一个手动启动的服务端。此图显示客户端如何在它的EncoderFactory代理上调用,从而产生一个隐式定位请求:

Initial architecture for the ripper application
Figure: Initial architecture for the ripper application

相应的客户端C++代码,如下所示:

// C++11
auto proxy = communicator->stringToProxy("factory@EncoderAdapter");
auto factory = Ice::checkedCast<Ripper::MP3EncoderFactoryPrx>(proxy);
auto encoder = factory->createEncoder();
//c++98
Ice::ObjectPrx proxy = communicator->stringToProxy("factory@EncoderAdapter");
Ripper::MP3EncoderFactoryPrx factory = Ripper::MP3EncoderFactoryPrx::checkedCast(proxy);
Ripper::MP3EncoderPrx encoder = factory->createEncoder();

请注意,客户端使用MP3EncoderFactory对象的间接代理。字符串化的代理可以被逐字地读为“在标识为EncoderAdapter的对象适配器中具有ID的factory的对象”。用于编码的服务端创建此对象适配器,并确保对象适配器使用此标识符。由于每个对象适配器都必须唯一定义,所以注册表可以轻松确定创建适配器的服务端,并向客户端返回正确的端点。

客户对checkedCast的调用是factory对象上的第一个远程调用;因此,定位请求在调用完成期间被执行。随后对createEncoder的调用直接发送到服务端,无需IceGrid的进一步介入。

3. Ripper注册表配置

注册表需要一个子目录来创建它的数据库,我们使用/opt/ripper/registry来实现此目的(在启动注册表之前,该目录必须存在)。我们还需要创建一个Ice配置文件来保存注册表所需的属性。文件/opt/ripper/registry.cfg包含以下属性:

IceGrid.Registry.Client.Endpoints=tcp -p 4061
IceGrid.Registry.Server.Endpoints=tcp
IceGrid.Registry.Internal.Endpoints=tcp
IceGrid.Registry.AdminPermissionsVerifier=IceGrid/NullPermissionsVerifier
IceGrid.Registry.LMDB.Path=/opt/ripper/registry
IceGrid.Registry.DynamicRegistration=1

有几个属性用于定义端点,但只有IceGrid.Registry.Client.Endpoints的值需要一个固定的端口。该属性指定IceGrid定位器服务端的端点(endpoints); IceGrid客户端必须在Ice.Default.Locator的定义中包含这些端点,如下一节所讨论的。本示例中使用的TCP端口(4061)已经由互联网号码分配机构(IANA)为IceGrid注册表保留,同时还有SSL端口号4062。

其他几个得一提的属性值:

  • IceGrid.Registry.AdminPermissionsVerifier

控制对注册表管理功能的访问。

  • IceGrid.Registry.LMDB.Path

指定注册表的数据库目录

  • IceGrid.Registry.DynamicRegistration

如果设置为非零,则允许服务端注册它们的对象适配器。动态注册在下面有更详细的解释。

默认情况下,没有使用IceGrid的部署工具,IceGrid将不允许服务端注册它的对象适配器。在某些情况下(例如在本示例应用程序中),你可能希望客户端能够间接地绑定到服务端,而无需先部署服务端。也就是说,简单地启动服务端应该足以使服务端通过IceGrid注册自己,并可从客户端访问。

你可以通过设置属性IceGrid.Registry.DynamicRegistration的值为非零在注册表中来达到目的。使用此设置,即使以前没有部署过,IceGrid也允许适配器在激活时自行注册。要强制服务端注册它的适配器,你必须定义Ice.Default.Locator(这样服务端才能找到注册表),对于你想注册的每个适配器,你必须设置<adapter-name>.AdapterId为一个在注册表中唯一的标识符。设置<adapter-name>.AdapterId属性还会导致适配器不再创建直接代理,而是创建间客户端必须通过注册表解析的间接代理。

4. Ripper客户端配置

客户端只需要最小的配置,即属性Ice.Default.Locator的值。该属性为Ice运行时间提供定位器服务的代理。在IceGrid中,定位器服务由注册表实现,定位器对象在注册表的客户端上可用。IceGrid.Registry.Client.Endpoints提供了构建代理所需的大部分信息。缺少的部分是定位器对象的ID,默认为IceGrid/Locator,但可能会根据注册表的配置更改:

Ice.Default.Locator=IceGrid/Locator:tcp -h registryhost -p 4061

定位器服务允许客户端利用间接绑定,并避免服务器端点上的静态依赖性。但是,定位器代理必须有一个固定的端口,否则客户端有自举(bootstrapping )问题:不知道定位器服务的端点,就不能解析间接代理。

如果用IceLocatorDiscovery,客户端不需定义Ice.Default.Locator属性,而是使用UDP的多播在运行时去发现注册表。

5. Ripper服务端配置

我们使用/opt/ripper/server.cfg作为服务端的配置文件。它包含以下属性:

EncoderAdapter.AdapterId=EncoderAdapter
EncoderAdapter.Endpoints=tcp
Ice.Default.Locator=IceGrid/Locator:tcp -h registryhost -p 4061

属性描述如下:

  • EncoderAdapter.AdapterId

这个属性提供对象描述符给客户端在间接代理中使用(比如:factory@EncoderAdapter)。

  • EncoderAdapter.Endpoints

这个属性定义对象适配器的端点。注意:这个值不包含任何端口的信息,意思是这个适配器使用系统分配的端口。如果没有IceGrid,使用系统分配端口会产生一个严重的问题:在服务端每次启动时适配器端口可能改变的情况下,客户如何端创建一个直接代理?IceGrid完美的决解了这个问题,因为客户端能使用没有端点依赖的间接代理。注册表使用对象适配器每次激活时提供的端点信息来解析间接代理。

  • Ice.Default.Locator

服务器需要此属性的值才能注册它的对象适配器。

6. 启动Ripper的注册表

现在配置文件和目录已经准备好,我们就可以启动IceGrid注册表:

icegridregistry --Ice.Config=/opt/ripper/registry.cfg

支持其他命令行选项,包括使用注册表作为Windows服务或Unix守护进程。

7. 启动Ripper的服务端

随着注册表的启动和运行,我们现在可以启动服务端了。在命令提示符下,我们运行程序并传递一个--Ice.Config的选项,来指定配置文件的位置:

$ /opt/ripper/bin/server --Ice.Config=/opt/ripper/server.cfg

8. Ripper进度回顾

这个例子演示了如何使用IceGrid的位置服务,它是IceGrid功能集的一个核心组件。通过将IceGrid与我们的应用程序的结合,客户端现在只需使用间接代理和Ice.Default.Locator的值来定位MP3EncoderFactory对象。此外,我们可以通过许多方式重新配置应用程序,而无需修改客户端的代码或配置。

对于某些应用程序,上面的演示程序已经满足需求了。不过,我们才刚刚开始探索IceGrid的能力,我们的运用程序仍然还有很大的提高空间。下一节将介绍如何通过将应用程序部署到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 ""