本节将介绍一个示例应用程序,来演示IceGrid的功能。
1. Ripper应用示例
我们的应用程序从光盘(CD)上读取音乐曲目,并将它们编码为MP3文件,如下所示:

翻录整张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
代理上调用,从而产生一个隐式定位请求:

相应的客户端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节点,来避免手动启动服务端。