4. 高级概念
4.1 网络传输
eProsima RPC over DDS通过eProsima Fast-DDS库提供一种网络传输实现方法。注意,Prosima RPC over DDS也支持RTIDDS,此发行版仅时用于eProsima Fast-DDS。如要使用RTIDDS,请从eProsima网站下载RTIDDS-based发行版。
4.1.1 RTPS传输
利用eProsima Fast-DDS提供的独立的RTPS协议实现(与其竞争对手相反),此传输在RTPS级别的代理和位于同一本地网络中的服务之间创建连接,从而消除了DDS的开销。服务和代理都实现此类项目。
RTPSProxyTransport
RTPSProxyTransport类实现了代理使用的RTPS传输:
class RTPSProxyTransport: public ProxyTransport
{
public:
RTPSProxyTransport(std::string remoteServiceName,
std::string instanceName,
int domainId = 0,
long timeout = 10000L);
virtual ~RTPSProxyTransport();
};
此类有一个构造函数,该构造函数将传输设置为使用 RTPS -level发现机制。此发现机制允许代理在本地网络查找外部元素。有三种潜在的场景:
- 在本地网络没有其他元素使用提供的服务名。这时,代理不会创建任何连接,直到另一个元素向网络宣布自己。如果客户端试图在这发生前调用远端程序,它会生成ServerNotFoundException异常.
- 在本地网络中单独的服务使用服务名。当代理创建时,它将找到服务并与它创建一个连接通道。当客户应用程序使用代理去访问远端程序时,此服务将会执行此程序并返回应答。
- 在本地网络中不止一个服务有相同的服务名。当用户希望拥有冗余服务器以避免系统故障时,可能会发生这种情况。当一个代理被创建时,它将查找所有的服务并与每个服务创建一个连接通道。当客户端应用程序使用代理访问远端程序时,所有的服务都会执行这个程序,但客户端将只收到一个服务端的一个应答。
使用示例中建议的IDL,开发者可以创建在本地网络中连接到特定服务端的代理。
BankProtocol *protocol = NULL;
RTPSProxyTransport *transport = NULL;
BankProxy *proxy = NULL;
try
{
protocol = new BankProtocol();
transport = new RTPSProxyTransport("BankService", "Instance");
proxy = new BankProxy(*transport, *protocol);
}
catch(eprosima::rpc::exception::InitializeException &ex)
{
std::cout << ex.what() << std::endl;
}
Account ac;
int32_t money;
ReturnCode depositRetValue;
try
{
depositRetValue = proxy->deposit(ac, money);
}
catch(eprosima::rpc::exception::Exception &ex)
{
std::cout << ex.what() << std::endl;
}
RTPSServerTransport
RTPSServerTransport类实现了一个被服务端使用的RTPS传输。
class RTPSServerTransport: public ServerTransport
{
public:
RTPSServerTransport(std:: string serviceName, std::string instanceName, int domainId = 0);
virtual ~RTPSServerTransport();
};
此类有一个构造函数,该构造函数使用 RTPS 发现机制设置传输。此机制允许服务去发现本地网络中的任何代理。
使用示例中建议的IDL,开发者将使用这些代码创建一个服务:
unsigned int threadPoolSize = 5;
ThreadPoolStrategy *pool = NULL;
BankProtocol *protocol = NULL;
RTPSServerTransport *transport = NULL;BankServer *server = NULL;
BankServerImplExample servant;try
{
pool = new ThreadPoolStrategy(threadPoolSize);
transport = new RTPSServerTransport("BankService", "Instance");
protocol = new BankProtocol();
server = new BankServer(*pool, *transport, *protocol, servant);
server->serve();
}
catch(eprosima::rpc::exception::InitializeException &ex)
{
std::cout << ex.what() << std::endl;
}
4.2 Asynchronous异步调用
eProsima RPC over DDS 支持异步调用:客户端应用程序可以在等待回复时调用远端程序而不阻塞执行。
4.2.1 异步调用远端程序
rpcddsgen为每一个远端程序产生一个异步调用方法,使用命名策略<RemoteProcedureName>_async。这些方法有两个参数:请求到达时将调用的对象和远端程序的输入参数。使用IDL示例,rpcddsgen生成如下的异步代理方法:
void deposit_async(Bank_depositCallbackHandler &obj, /*in*/ const Account& ac, /*in*/ int32_t money);
异步版本的远端程序会抛出如下异常:
Exception | Description |
eprosima::rpc::exception::ClientException | 当客户端出现问题时会抛出此异常。 |
eprosima::rpc::exception::ServerNotFoundException | 当代理找不到服务时会抛出此异常。 |
Example:
class Bank_depositHandler: public depositCallbackHandler { void deposit(/*out*/ ReturnCode deposit_ret) { // Client desired behaviour when the reply arrives }
virtual void on_exception(const eprosima::rpc::exception::Exception &ex) { // Client desired behaviour on exception } } void main() { RTPSProxyTransport *transport = NULL; BankProtocol *protocol = NULL; BankProxy *proxy = NULL; try { transport = new RTPSProxyTransport("BankService", "Instance"); protocol = new BankProtocol(); proxy = new BankProxy(*transport, *protocol); } catch(eprosima::rpc::exception::InitializeException &ex) { std::cout << ex.what() << std::endl; }
Account ac; int32_t money = 0; Bank_depositHandler deposit_handler; try { proxy->deposit_async(deposit_handler, ac, money); } catch(eprosima::rpc::exception::Exception &ex) { std::cout << ex.what() << std::endl; } } |
4.2.2 应答回调对象
当服务端通过对象(开发者传递一个参数给异步调用的对象)进行回复时,客户端会收到通知。rpcddsgen为每个远端程序生成一个支持异步调用的抽象类。这些类的命名策略是<InterfaceName>_<RemoteProcedureName>CallbackHandler。
在这些类中会创建两个抽象方法:
- 成功的应答到达时调用的回调,使用来自服务端的返回值作为输入参数
- 异常和错误管理的回调。
用户必须创建一个继承<InterfaceName>_<RemoteProcedureName>CallbackHandler的类才能实现这两个方法。
使用IDL示例,rpcddsgen生成如下代码:
class Bank_depositCallbackHandler { public: virtual void deposit( /*out*/ ReturnCode deposit_ret) = 0; virtual void error(const eprosima::rpc::exception::Exception &ex) = 0; }; |
发生异常时调用的函数应该能够管理以下异常:
Error code | Description |
eprosima::rpc::exception::ClientInternalException | 客户端发生异常。 |
eprosima::rpc::exception::ServerTimeoutException | 超过等待服务端应答的最大时间。 |
eprosima::rpc::exception::ServerInternalException | 服务端发生异常。 |
4.3 单向调用
有时远端程序不需要服务端的应答。这时,eProsima RPC over DDS 支持单向调用。
开发者可以定义远端程序为单向调用,当客户端程序调用远端程序时,线程不会等待任何应答。
若要创建单向调用,必须在 IDL 文件中使用以下规则定义远端程序:
- oneway保留字必须放在函数定义之前。
- 函数的返回值必须是void类型。
- 函数不能有inout或out参数。
下面示例显示了如何使用 IDL 定义单向程序:
interface Bank
{
oneway void deposit(in Account ac, in long money);
};
4.4 线程服务策略
eProsima RPC over DDS 库为服务端提供若干个线程策略。
4.4.1 单线程策略
在此策略中,服务端仅使用一个线程进行请求管理。服务端一次仅执行一个请求。服务端用于处理请求的线程与接收线程相同。若要使用单线程策略,创建的服务的构造函数参数含有 SingleThreadStrategy对象。
SingleThreadStrategy *single = NULL;
BankProtocol *protocol = NULL;
TPCServerTransport *transport = NULL;BankServer *server = NULL;
BankServerImplExample servant;try
{
single = new SingleThreadStrategy();
transport = new TCPServerTransport("127.0.0.1:8080");
protocol = new BankProtocol();
server = new BankServer(*single, *transport, *protocol, servant);
server->serve();
}
catch(eprosima::rpc::exception::InitializeException &ex)
{
std::cout << ex.what() << std::endl;
}
4.4.2 线程池策略
在这种情况下,服务端管理用于处理传入请求的线程池。每次请求到达时,服务器都会将其分配给线程池中的空闲线程。
若要使用线程池策略,创建的服务的构造函数参数含有 ThreadPoolStrategy 对象。
unsigned int threadPoolSize = 5;
ThreadPoolStrategy *pool = NULL;
BankProtocol *protocol = NULL;
TCPServerTransport *transport = NULL;BankServer *server = NULL;
BankServerImplExample servant;try
{
pool = new ThreadPoolStrategy(threadPoolSize);
transport = new TCPServerTransport("127.0.0.1:8080");
protocol = new BankProtocol();
server = new BankServer(*pool, *transport, *protocol, servant);
server->serve();
}
catch(eprosima::rpc::exception::InitializeException &ex)
{
std::cout << ex.what() << std::endl;
}
4.4.3 每个请求一个线程策略
在这种情况下,服务端为每一个到来的请求创建一个新线程。这是资源最密集的备选方案。
若使用这种策略,创建的服务的构造函数参数含有ThreadPerRequestStrategy对象。
ThreadPerRequestStrategy *perRequest = NULL;
BankProtocol *protocol = NULL;
TCPerverTransport *transport = NULL;BankServer *server = NULL;
BankServerImplExample servant;try
{
perRequest = new ThreadPerRequestStrategy();
transport = new TCPServerTransport("127.0.0.1:8080");
protocol = new BankProtocol();
server = new BankServer(*perRequest, *transport, *protocol, servant);
server->serve();
}
catch(eprosima::rpc::exception::InitializeException &ex)
{
std::cout << ex.what() << std::endl;
}