[翻译]Iceoryx - 请求-响应通信模式

1. 概述

本文原英文文章地址:design/request_response_communication.md
除了Iceoryx最初支持的发布-订阅消息模式外,请求-响应模式在Iceoryx所面向的领域中也被广泛使用。由于我们旨在与ROS 2进行良好集成,因此也需要支持其Service通信方法,该方法通常用于控制机器人。

发布-订阅是一种数据分发模式,它会不断地提供数据更新,而请求-响应则是一种远程过程调用和任务分发模式,仅在需要时提供数据。这意味着有一个或多个客户端请求服务器执行任务。

在Iceoryx中,客户端和服务器将基于我们已经用于发布-订阅的相同构建块构建,如ChunkSenderChunkReceiver

2. 术语

名称描述
请求-响应按需提供数据的通信模式
发布-订阅不断提供数据更新的通信模式
RPC远程过程调用,将任务卸载给服务器
客户端调用RPC的实体
服务器处理RPC的实体

3. 设计

3.1 考虑事项

客户端和服务器应能够与WaitSetListener结合使用,以提供基于事件的通信手段,而不是轮询。

与发布-订阅一样,ServiceDescription将用于将客户端连接到相应的服务器。

为了支持异步请求,每个请求应包含一个序列ID,该ID将被复制到相应的响应中。

3.2 解决方案

这是无类型ClientServer类的概述。

在这里插入图片描述

ClientServer重用了ChunkSenderChunkReceiver构建块。Client使用ChunkSender发送请求并使用ChunkReceiver获取响应,而Server使用ChunkReceiver获取请求并使用ChunkSender发送响应。

类型化API

在这里插入图片描述

由于Response与特定的Request绑定,loan方法会接受一个Request以使用正确的设置填充Response。或者,Request可以有一个createResponse方法,返回具有正确设置的Response

无类型API

在这里插入图片描述

与类型化API类似,loan接受一个指向RequestHeader的指针,以使用正确的设置填充ResponseHeader

客户端端口

在这里插入图片描述

ClientPortData位于共享内存中,仅包含数据而不包含访问方法。ClientPortUser是为用户提供访问方法的类,ClientPortRouDi提供了RouDi需要的接口,以便将客户端连接到服务器并清理端口资源。

服务器端口

在这里插入图片描述

与客户端端口类似,服务器端口有ServerPortData,它位于共享内存中,仅包含数据而不包含访问方法。ServerPortUser是为用户提供访问方法的类,ServerPortRouDi提供了RouDi需要的接口,以便在服务器提供服务后将客户端连接到服务器,并清理端口资源。

必须确保在任何时候只能有一个具有给定ServiceDescription的服务器运行。

请求/响应头

在这里插入图片描述

由于请求和响应需要编码不同的元信息,我们也需要不同的消息头。公共数据聚合在RpcBaseHeader中,它包含一个指向ClientChunkQueueData_tiox::UniqueId和一个序列ID。iox::UniqueId用于标识接收响应的队列,并用作ChunkDistributor::deliverToQueue的标识符。此方法将遍历所有存储的队列,并匹配ChunkQueueData::m_uniqueIdOfOwner。作为优化,lastKnownQueueIndex将用于快速查找,并在队列ID不匹配时降级为完整查找。

class ChunkDistributor {
    ...
    iox::expected<Error> deliverToQueue(iox::UniqueId uniqueQueueId, uint32_t lastKnownQueueIndex, mepoo::SharedChunk chunk);
    ...
};

序列ID用于在异步调用多个请求时将响应与特定请求匹配。根据客户端和服务器的选项,请求可能会被丢弃,或者服务器可能有一个工作池,从而导致响应的发送顺序与请求的接收顺序不同。序列ID必须由用户设置,并在响应时由用户检查。

客户端/服务器选项

在这里插入图片描述

客户端和服务器选项可用于控制客户端和服务器的某些方面。除了设置队列的容量和定义客户端是否应在创建时连接以及服务器是否应在创建时提供服务,还可以定义对慢速客户端和服务器的行为。客户端可以要求服务器在其响应队列已满时阻塞。如果相应的clientTooSlowPolicy设置为ConsumerTooSlowPolicy::WAIT_FOR_CONSUMER,服务器将遵守。如果选项不匹配,客户端将无法连接到服务器,这与发布者和订阅者的行为类似。

客户端服务发现

客户端由以下状态机引导。

在这里插入图片描述

类似于订阅者状态机,客户端通过CONNECT CaPro消息将其响应队列传递给服务器。服务器通过ACK CaPro消息将其请求队列传递给客户端。

以下是显示所有这些情况的序列图

在这里插入图片描述

4. 未解决的问题

  • 集成到端口自省中
  • 集成到网关中,例如DDS网关
下面是一个iceoryx发布订阅的C代码示例,其中一个发布者发布一个消息,而两个订阅者订阅这个消息: ```c #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include "iceoryx_posh/popo/subscriber.hpp" #include "iceoryx_posh/popo/publisher.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" #include "iceoryx_posh/roudi/introspection_types.hpp" int main() { // 初始化iceoryx runtime iox::runtime::PoshRuntime::initRuntime("publisher"); // 创建发布者和订阅者 iox::popo::Publisher publisher({"Radar", "FrontLeft", "Object"}); iox::popo::Subscriber subscriber1({"Radar", "FrontLeft", "Object"}); iox::popo::Subscriber subscriber2({"Radar", "FrontLeft", "Object"}); // 订阅者1等待消息 subscriber1.subscribe(); printf("Subscriber 1 waiting for messages...\n"); // 订阅者2等待消息 subscriber2.subscribe(); printf("Subscriber 2 waiting for messages...\n"); // 发布者发布消息 printf("Publisher publishing message...\n"); publisher.publish("Hello, world!"); // 等待订阅者接收消息 while (true) { if (subscriber1.hasData()) { printf("Subscriber 1 received message: %s\n", subscriber1.getChunk()->userPayload()); break; } if (subscriber2.hasData()) { printf("Subscriber 2 received message: %s\n", subscriber2.getChunk()->userPayload()); break; } } // 清理资源并退出 iox::runtime::PoshRuntime::shutdownRuntime(); return 0; } ``` 需要注意的是,这个示例代码需要使用iceoryx库,需要将iceoryx库链接到您的项目中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值