Remote SOF - 支持分布式软件模块的 OSGI 类模块化框架(C++)。
本文介绍了名为 Remote SOF 的模块化框架的用法,该框架支持分布式软件模块。
引言
Remote SOF (Remote Service Oriented Framework) 是 SOF (Service Oriented Framework) 的一个扩展。在阅读本文之前,我建议您在此处 阅读有关 SOF 的文章,或访问 网站 上的文档,其中描述了 SOF 框架的基本机制。Remote SOF 像 SOF 一样,通过将代码划分为多个组件(称为“bundle”),并允许这些组件通过明确定义的接口(称为“service”)进行通信,来支持软件的模块化。使用 SOF 时,不同“bundle”之间的通信仅限于同一进程。这意味着,只有同一进程中的“bundle”才能相互调用。
Remote SOF 更进一步,允许在不同进程中运行的“bundle”之间进行通信。
上图显示了 Remote SOF 的基本架构。与仅有一个进程的 SOF 不同,Remote SOF 可以存在多个 SOF 容器(进程)。SOF 框架的注册表组件是核心管理组件,用于保存所有已注册服务和服务侦听器的信息。对于 Remote SOF,这些信息由远程注册表组件分发到所有 SOF 容器的每个“本地”注册表组件。例如,“bundle 1”知道“bundle 3”注册的服务和服务侦听器,反之亦然。如果“bundle 1”注册了一个“bundle 3”感兴趣的服务,“bundle 3”将在服务注册后立即收到通知。当然,Remote SOF 也允许“bundle”调用同一进程内的“bundle”的服务。
CORBA(特别是 CORBA 实现 MICO)充当 Remote SOF 项目的通信层。这意味着可远程调用的服务对象、可远程调用的服务对象侦听器以及远程注册表都实现了为 CORBA 对象。Remote SOF 尽量隐藏 CORBA 基础设施,但在某些情况下(例如,窄化 CORBA 对象、使用 CORBA 类型等),您需要了解 CORBA 基础知识。
注意:如果您只想实现“本地”包(所有包都在同一进程中运行),请使用 SOF。要实现分布式软件包和可远程调用的服务,您必须使用 Remote SOF。在 Remote SOF 的未来版本中,计划支持在一个 SOF 容器中混合运行“本地”和可远程调用的包。
使用代码
构建软件
Remote SOF 的实现与操作系统无关。但是,目前只有一个 Visual Studio 项目文件可用于构建软件。请查看项目的网页以获取更多信息。未来计划为其他平台提供 make 文件。
与 SOF 有何不同?
由于尽可能重用了 SOF 的源代码来实现 Remote SOF,因此 Remote SOF 的 API 与 SOF 的 API 差异不大。
变化如下:
- 服务接口不再实现为 C++ 接口,而是实现为 CORBA IDL 接口。
- 使用
IRemoteBundleActivator
实现包,而不是IBundleActivator
。 使用
IRemoteBundleContext
而不是IBundleContext
来注册服务。- 使用
RemoteServiceTracker
而不是ServiceTracker
来创建服务跟踪器。 - 实现
IRemoteServiceTracker
接口而不是IServiceTracker
来跟踪服务。
在以下部分,将重用 SOF 文章中的示例来解释 Remote SOF。示例中所有相关的源代码都可以在“sof/remote/examples”目录下找到。还有两个名为“bundle1”和“bundle2”的包,它们通过类型为 IMultiplier
的服务接口进行通信。与 SOF 示例不同,这两个包在不同的进程中运行。
实现 IRemoteBundleActivator 接口
IRemoteBundleActivator
接口提供了与 IBundleActivator
接口相同的函数(用于清理使用资源的析构函数、用于启动包的 start
函数和用于停止包的 stop
函数)。与 SOF 不同,用于注册和注销服务的 start
和 stop
函数的参数类型不是 IBundleContext
,而是 IRemoteBundleContext
。以下示例显示了“bundle1”的 IRemoteBundleActivator
接口实现。析构函数以及 start
和 stop
函数将在稍后填充代码。
头文件:
#ifndef BUNDLE_ACTIVATOR1_H
#define BUNDLE_ACTIVATOR1_H
#include "sof/framework/remote/corba/IRemoteBundleActivator.h"
#include "sof/framework/remote/corba/IRemoteBundleContext.h"
using namespace sof::framework::remote::corba;
class BundleActivator1 : public IRemoteBundleActivator
{
public:
virtual ~BundleActivator1();
virtual void start( IRemoteBundleContext::ConstPtr context );
virtual void stop( IRemoteBundleContext::ConstPtr context );
};
#endif
实现:要注册包激活器类的类型和名称,必须使用宏 REGISTER_REMOTE_BUNDLE_ACTIVATOR_CLASS
,而不是 REGISTER_BUNDLE_ACTIVATOR_CLASS
。
#include "BundleActivator1.h"
#include "sof/instantiation/ObjectCreator.h"
#include "sof/framework/Properties.h"
using namespace sof::instantiation;
using namespace sof::framework;
BundleActivator1::~BundleActivator1()
{
// Deallocate memory
}
void BundleActivator1::start(IRemoteBundleContext::ConstPtr context)
{
// Add code for registering services and service listeners}
}
void BundleActivator1::stop(IRemoteBundleContext::ConstPtr context)
{
// Add code for deregistering services and service listeners}
}
REGISTER_REMOTE_BUNDLE_ACTIVATOR_CLASS( "BundleActivator1", BundleActivator1 )
服务
可被其他包远程调用的服务的接口必须在 CORBA IDL(= Interface Definition Language)中定义,该语言独立于任何编程语言(C++、Java 等)。本示例中使用的乘法器服务定义如下:
#include "../../idl/CORBAObjects.idl"
interface Multiplier : sof::framework::remote::corba::generated::CORBAService
{
long multiply( in long x, in long y );
};
机制与 SOF 非常相似。
- 您必须定义服务接口(SOF 中使用 C++,Remote SOF 中使用 IDL)。
- 服务接口必须继承自基础接口(SOF 继承自
IService
,Remote SOF 继承自CORBAService
)。
CORBAService
类型在 IDL 文件 'CORBAObjects.idl' 中定义,该文件随 Remote SOF 软件分发并放置在 'sof/remote/idl' 目录中。在此 IDL 文件中,您可以找到所有可远程调用对象的接口定义。
在 IDL 中定义服务接口后,必须通过 IDL 编译器生成特定于语言的代码。在 'sof/remote/examples/idl' 目录中有一个名为 'gen_multiplier.bat' 的 Windows Shell 脚本,它会生成 'Mutiplier.h' 和 'Multiplier.cpp' 文件,并将它们复制到 'sof/remote/examples/common/src' 目录中。生成的代码包含 C++ 服务接口(Multiplier
)、存根(Multiplier_stub
)和骨架(POA_Multiplier
)实现,其中存根和骨架封装了通信细节。存根在客户端替换远程对象,并通过网络连接将所有调用转发到服务器端的骨架。然后,服务器端的骨架调用远程对象。第一个包('bundle1')必须通过继承生成的类型 POA_Multiplier
来实现 C++ 服务接口。
头文件:
#ifndef MULTIPLIER_IMPL_H
#define MULTIPLIER_IMPL_H
#include "Multiplier.h"
using namespace std;
class MultiplierImpl : virtual public POA_Multiplier
{
public:
virtual CORBA::Long multiply( CORBA::Long x, CORBA::Long y );
};
#endif
实现:
#include <CORBA.h>
#include "MultiplierImpl.h"
#include <iostream>
using namespace std;
CORBA::Long MultiplierImpl::multiply( CORBA::Long x, CORBA::Long y )
{
cout << "Multiplier called. " << endl;
return x*y;
}
注册和注销服务
现在,我们准备好注册服务对象了。在这里,BundleActivator1
类注册了两个乘法器服务实例。在头文件中为每个服务实例定义了服务对象(MultiplierImpl
)和注册对象(IServiceRegistration
)的成员变量。
头文件:
#ifndef BUNDLE_ACTIVATOR1_H
#define BUNDLE_ACTIVATOR1_H
#include "sof/framework/remote/corba/IRemoteBundleActivator.h"
#include "sof/framework/remote/corba/IRemoteBundleContext.h"
#include "sof/framework/IServiceRegistration.h"
#include "MultiplierImpl.h"
using namespace sof::framework::remote::corba;
class BundleActivator1 : public IRemoteBundleActivator
{
private:
IServiceRegistration* serviceReg1;
MultiplierImpl* service1;
IServiceRegistration* serviceReg2;
MultiplierImpl* service2;
public:
virtual ~BundleActivator1();
virtual void start( IRemoteBundleContext::ConstPtr context );
virtual void stop( IRemoteBundleContext::ConstPtr context );
};
#endif
在 BundleActivator1
类的后续实现中,start
函数设置服务实例的属性并创建服务对象。之后,通过调用 registerRemoteService
(而不是 SOF 中的 registerService
)来注册这两个服务实例。
从现在起,其他包可以跟踪和调用服务实例了。
实现:
#include "BundleActivator1.h"
#include "sof/instantiation/ObjectCreator.h"
#include "sof/framework/Properties.h"
using namespace sof::instantiation;
using namespace sof::framework;
BundleActivator1::~BundleActivator1()
{
// Deallocate memory
}
void BundleActivator1::start(IRemoteBundleContext::ConstPtr context)
{
Properties props;
props.put( "instance", "1" );
this->service1 = new MultiplierImpl();
this->serviceReg1 =
context->registerRemoteService( "Multiplier", this->service1, props );
props.put( "instance", "2" );
this->service2 = new MultiplierImpl();
this->serviceReg2 =
context->registerRemoteService( "Multiplier", this->service2, props );
}
void BundleActivator1::stop(IRemoteBundleContext::ConstPtr context)
{
this->serviceReg1->unregister();
delete this->serviceReg1;
delete this->service1;
this->serviceReg2->unregister();
delete this->serviceReg2;
delete this->service2;
}
REGISTER_REMOTE_BUNDLE_ACTIVATOR_CLASS( "BundleActivator1", BundleActivator1 )
注册和注销服务侦听器
正如在 SOF 文章中学习到的,可以通过创建跟踪器对象来查找服务。为此,Remote SOF 提供了 RemoteServiceTracker
类。与 SOF 的 ServiceTracker
类一样,RemoteServiceTracker
类在构造函数中需要三个参数:
- 类型为
IRemoteBundleContext
的包上下文对象。 - 需要查找的服务名称。
- 实现
IRemoteServiceTrackerCustomizer
接口的对象。
以下 BundleActivator2
类的实现演示了如何创建和使用服务跟踪器对象来查找已注册的服务。与 SOF 在 addingService
函数中传递 ServiceReference
对象不同,这里传递的是 RemoteServiceReference
对象,该对象封装了远程服务的特性(服务名称、属性、服务对象引用)。要调用远程服务,必须将远程服务对象的引用(类型为 CORBAService_var
)窄化(类似于 C++ 对象的强制类型转换)到正确的服务对象类型(Multiplier_var
)。之后,就可以调用服务了。
实现:
#include "BundleActivator2.h"
#include "sof/instantiation/ObjectCreator.h"
#include "sof/framework/Properties.h"
#include "sof/framework/remote/corba/RemoteServiceReference.h"
using namespace sof::instantiation;
using namespace sof::framework;
using namespace sof::framework::remote::corba;
BundleActivator2::~BundleActivator2()
{
// Deallocate memory
}
void BundleActivator2::start(IRemoteBundleContext::ConstPtr context)
{
this->tracker = new RemoteServiceTracker( context,
"Multiplier", this );
this->tracker->startTracking();
}
void BundleActivator2::stop(IRemoteBundleContext::ConstPtr context)
{
this->tracker->stopTracking();
delete ( this->tracker );
}
bool BundleActivator2::addingService( const RemoteServiceReference& ref )
{
cout << "[BundleActivator2#addingService] Called." << endl;
if ( ref.getServiceName() == "Multiplier" )
{
Properties props = ref.getServiceProperties();
cout << "[BundleActivator2#addingService] Multiplier instance found." << endl;
cout << "[BundleActivator2#addingService] Properties: "
<< props.toString() << endl;
cout << "[BundleActivator2#addingService] Service reference: "
<< ref.toString() << endl;
Multiplier_var multiplier = Multiplier::_narrow( ref.getRemoteService() );
CORBA::Long result = multiplier->multiply( 8, 15 );
cout << "Result: " << result << endl;
return true;
}
else
{
return false;
}
}
void BundleActivator2::removedService( const RemoteServiceReference& ref )
{
cout << "[BundleActivator2#removedService] Called, ref: " << ref.toString() << endl;
}
REGISTER_REMOTE_BUNDLE_ACTIVATOR_CLASS( "BundleActivator2", BundleActivator2 )
创建包库
现在,可以测试已实现的两个包了。为此,有两种可能性:
- 包构建为 DLL,可以通过 Remote SOF 的交互式控制台应用程序启动(类似于 SOF 文章中的示例)。
- 实现了一个主函数,该函数启动
RemoteSOFLauncher
类以在启动时加载包。
在这里,对于此示例,再次选择了第一种可能性,并且这是使包准备好作为 Windows DLL 加载的代码:
实现(dll.cpp)
#include <windows.h>
#include "sof/instantiation/ObjectCreator.h"
#include "sof/framework/remote/corba/IRemoteBundleActivator.h"
#define DLL extern "C" __declspec(dllexport)
using namespace sof::framework;
using namespace sof::framework::remote::corba;
using namespace sof::instantiation;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
DLL IRemoteBundleActivator* createObject( const string &className )
{
ObjectCreator<IRemoteBundleActivator> OC_BUNDLE_ACTIVATOR;
return OC_BUNDLE_ACTIVATOR.createObject( className );
}
与 SOF 一样,必须实现一个 createObject
函数,该函数返回已加载包的包激活器实例。与 SOF 不同,该函数返回 IRemoteBundleActivator
的实例,而不是 IBundleActivator
的实例。'dll.cpp' 文件可以重用于实现作为 Windows DLL 加载的其他包。
下图(Visual Studio 项目文件快照)显示了属于两个包实现的所有文件:
每个包包含一个包激活器类(BundleActivato1
、BundleActivator2
)用于注册服务和服务侦听器,以及乘法器接口类(Multiplier.h、Multiplier.cpp)。'dll.cpp' 允许将包作为 Windows DLL 加载。此外,'bundle1' 提供了乘法器接口的实现(MultiplierImpl.h、MultiplierImpl.cpp)。
将两个包构建为 Windows DLL 后,就可以测试这些包了,这将在下一节中介绍。
测试包
通常,在可以启动 Remote SOF 容器之前,必须启动 CORBA 命名服务和远程注册表进程(请参阅最开始的 Remote SOF 图)。
CORBA 命名服务
CORBA 命名服务是 CORBA 中指定的一项服务。它允许您将抽象名称与 CORBA 对象关联起来,并允许客户端通过查找相应的名称来查找这些对象。使用的 CORBA 实现 MICO 提供了一个 CORBA 命名服务,可以如下启动:
- 打开 Windows 命令提示符。
- 切换到目录 'sof\remote\registry\bin'。
- 输入 run_ns.bat,它会执行 nsd.exe -ORBIIOPAddr inet:localhost:5000。
注意:传递的参数 '-ORBIIOPAddr inet:localhost:5000' 定义了命名服务应运行的 IP 地址和端口号。这里选择的是 'localhost' 和端口 '5000'。
远程注册表
要启动远程注册表进程:
- 打开 Windows 命令提示符。
- 切换到目录 'sof\remote\registry\bin'。
- 输入 run_registry.bat,它会执行 registry.exe -ORBNamingAddr inet:localhost:5000。
注册表可执行文件将在之前启动的 CORBA 命名服务处注册一个远程注册表对象。
启动测试包
首先,必须为每个测试包启动一个 Remote SOF 容器:
- 打开 Windows 命令提示符。
- 切换到目录 'sof\remote\console\bin'。
- 输入 run_remote_console.bat,它会启动 Remote SOF 框架和一个用于输入命令的用户界面(例如,用于启动和停止包)。
下图说明了迄今为止已执行的操作。首先,启动了 CORBA 命名服务。然后,启动了注册表,它创建了一个远程注册表对象(= CORBA 对象)并在此对象在 CORBA 命名服务中注册。最后一步,创建了 Remote SOF 容器,它们在远程注册表中注册了一个观察者对象。
现在,可以加载测试包了。请输入:
>
stbdll bundle1 BundleActivator1
<SOF_HOME>/remote/examples/bundle1/bin remote_bundle1.dll
在第一个 Remote SOF 容器的控制台上。输入此命令后,将加载第一个包,该包注册一个名为 'Multiplier' 的服务对象。
要启动第二个测试包,您需要输入:
> stbdll bundle2
BundleActivator2 <SOF_HOME>/remote/examples/bundle2/bin
remote_bundle2.dll
在第二个 Remote SOF 控制台上。当然,也可以将两个包加载到同一个容器中。第二个包跟踪第一个测试包的服务,并被通知可用的服务对象。'Bundle2' 调用 'bundle1' 的服务对象。在第一个控制台中,'bundle1' 打印出消息 'Multiplier called.',表示服务对象已被调用。第二个控制台的 'Bundle2' 打印出乘法结果。
注意:两个包的启动顺序无关紧要('bundle1' 在 'bundle2' 之前或反之亦然)。
结论
本文简要介绍了如何使用 Remote SOF 实现分布式包,而 Remote SOF API 与 SOF API 差别不大。请参阅 项目网站以获取更多文档。
未来,我们考虑实现以下问题:
- 目前,您必须决定是实现分布式包(使用 Remote SOF 框架)还是“本地”包(使用 SOF 框架)。目标是在一个 SOF 容器中实现分布式和本地包的混合。
- 平台无关的 make 文件。
- 用于监控 Remote SOF 进程(远程 SOF 容器、远程注册表等)的 GUI。
- 支持 CORBA 之外的其他通信层(例如 ICE)。
- 使 CORBA 命名服务可选择,以简化启动。
历史
- 2009 年 7 月 8 日 - 创建文章。