【FastDDS】概述 Library Overview
Fast DDS库概述
一、总览
Fast DDS(前身为Fast RTPS)是DDS规范的一种高效且高性能的实现,是用于分布式应用程序软件的以数据为中心的通信中间件(DCPS)。本节将回顾Fast DDS的架构、操作和主要特性。
二、架构
Fast DDS的架构如下图所示,从中可以看到一个分层模型,包含以下不同层级:
- 应用层:利用Fast DDS API实现分布式系统通信的用户应用程序。
- Fast DDS层:DDS通信中间件的稳健实现。它允许部署一个或多个DDS域,同一域内的域参与者通过在域主题下发布/订阅来交换消息。
- RTPS层:实时发布-订阅(RTPS)协议的实现,用于与DDS应用程序的互操作性。该层充当传输层的抽象层。
- 传输层:Fast DDS可在各种传输协议上使用,如不可靠传输协议(UDP)、可靠传输协议(TCP)或共享内存传输协议(SHM)。
2.1 DDS层
Fast DDS的DDS层中定义了多个通信关键元素。用户将在其应用程序中创建这些元素,从而整合DDS应用程序元素并创建以数据为中心的通信系统。遵循DDS规范,Fast DDS将这些参与通信的元素定义为实体(Entities)。DDS实体是任何支持服务质量(QoS)配置并实现监听器(Listener)的对象。
- QoS:定义每个实体行为的机制。
- 监听器:实体获知应用程序执行过程中可能出现的事件的机制。
下面列出了DDS实体及其描述和功能。有关每个实体、其QoS和监听器的更详细解释,请参考DDS层部分。
- 域(Domain):标识DDS域的正整数。每个域参与者都有一个指定的DDS域,以便同一域中的域参与者可以通信,并隔离不同DDS域之间的通信。应用程序开发人员在创建域参与者时必须提供此值。
- 域参与者(DomainParticipant):包含其他DDS实体(如发布者、订阅者、主题和多主题)的对象。它是允许创建其所包含的上述实体并配置其行为的实体。
- 发布者(Publisher):发布者使用数据写入器在某个主题下发布数据,数据写入器将数据写入传输层。它是创建和配置其所包含的数据写入器实体的实体,可包含一个或多个数据写入器。
- 数据写入器(DataWriter):负责发布消息的实体。用户在创建该实体时必须提供一个主题,数据将在该主题下发布。发布通过将数据对象作为数据写入器历史(DataWriterHistory)中的变更来完成。
- 数据写入器历史(DataWriterHistory):这是数据对象变更的列表。当数据写入器在特定主题下发布数据时,实际上是在该数据中创建了一个变更。正是这个变更被记录在历史中。这些变更随后会被发送到订阅该特定主题的数据读取器。
- 订阅者(Subscriber):订阅者使用数据读取器订阅某个主题,数据读取器从传输层读取数据。它是创建和配置其所包含的数据读取器实体的实体,可包含一个或多个数据读取器实体。
- 数据读取器(DataReader):为接收发布内容而订阅主题的实体。用户在创建该实体时必须提供一个订阅主题。数据读取器将接收到的消息作为其数据读取器历史(DataReaderHistory)中的变更。
- 数据读取器历史(DataReaderHistory):包含数据读取器由于订阅某个主题而接收到的数据对象的变更。
- 主题(Topic):将发布者的数据写入器与订阅者的数据读取器绑定在一起的实体。
2.2 RTPS层
如上所述,Fast DDS中的RTPS协议允许DDS应用程序实体与传输层抽象开来。根据上图,RTPS层有四个主要实体。
- RTPS域(RTPSDomain):是DDS域向RTPS协议的扩展。
- RTPS参与者(RTPSParticipant):包含其他RTPS实体的实体。它允许配置和创建其所包含的实体。
- RTPS写入器(RTPSWriter):消息的来源。它读取数据写入器历史中写入的变更,并将其传输到所有之前已匹配的RTPS读取器。
- RTPS读取器(RTPSReader):消息的接收实体。它将RTPS写入器报告的变更写入数据读取器历史。
有关每个实体、其属性和监听器的更详细解释,请参考RTPS层部分。
2.3 传输层
Fast DDS支持在多种传输协议上实现应用程序。这些协议包括UDPv4、UDPv6、TCPv4、TCPv6和共享内存传输(SHM)。默认情况下,一个域参与者会实现UDPv4和SHM传输协议。所有支持的传输协议的配置在传输层部分有详细说明。
三、编程和执行模型
Fast DDS是并发且基于事件的。下面将解释支配Fast DDS操作的多线程模型以及可能出现的事件。
3.1 并发和多线程
Fast DDS实现了一个并发多线程系统。每个域参与者会生成一组线程来处理后台任务,如日志记录、消息接收和异步通信。这不应影响您使用该库的方式,即Fast DDS API是线程安全的,因此您可以放心地从不同线程调用同一个域参与者上的任何方法。然而,当外部函数访问被库内部运行的线程修改的资源时,必须考虑这种多线程实现。例如,实体监听器回调中被修改的资源就是这种情况。
下面列出了Fast DDS生成的完整线程集。与传输相关的线程(标记为UDP、TCP和SHM类型)仅在使用相应的传输时才会创建。
名称 | 类型 | 数量 | 操作系统线程名称 | 描述 |
---|---|---|---|---|
事件(Event) | 通用 | 每个域参与者一个 | dds.ev.<participant_id> | 处理周期性和触发的时间事件。参见DomainParticipantQos。 |
发现服务器事件(Discovery Server Event) | 通用 | 每个域参与者一个 | dds.ds_ev.<participant_id> | 同步对发现服务器数据库的访问。参见DomainParticipantQos。 |
异步写入器(Asynchronous Writer) | 通用 | 每个启用的异步流控制器一个。至少1个。 | dds.asyn.<participant_id>.<async_flow_controller_index> | 管理异步写入。即使对于同步写入器,某些通信形式也必须在后台启动。参见DomainParticipantQos和FlowControllersQos。 |
数据共享监听器(Datasharing Listener) | 通用 | 每个数据读取器一个 | dds.dsha.<reader_id> | 处理通过数据共享接收的消息的监听器线程。参见DataReaderQos。 |
接收(Reception) | UDP | 每个端口一个 | dds.udp.<port> | 处理传入UDP消息的监听器线程。参见TransportConfigQos和UDPTransportDescriptor。 |
接收(Reception) | TCP | 每个TCP连接一个 | dds.tcp.<port> | 处理传入TCP消息的监听器线程。参见TCPTransportDescriptor。 |
接受(Accept) | TCP | 每个TCP传输一个 | dds.tcp_accept | 处理传入TCP连接请求的线程。参见TCPTransportDescriptor。 |
保活(Keep Alive) | TCP | 每个TCP传输一个 | dds.tcp_keep | TCP连接的保活线程。参见TCPTransportDescriptor。 |
接收(Reception) | SHM | 每个端口一个 | dds.shm.<port> | 处理通过SHM段传入的消息的监听器线程。参见TransportConfigQos和SharedMemTransportDescriptor。 |
日志记录(Logging) | SHM | 每个端口一个 | dds.shmd.<port> | 将传输的数据包存储并转储到文件中。参见TransportConfigQos和SharedMemTransportDescriptor。 |
看门狗(Watchdog) | SHM | 一个 | dds.shm.wdog | 监控打开的共享内存段的健康状况。参见TransportConfigQos和SharedMemTransportDescriptor。 |
通用日志记录(General Logging) | 日志 | 一个 | dds.log | 累积日志条目并将其写入适当的消费者。参见Logging Thread。 |
安全日志记录(Security Logging) | 日志 | 每个域参与者一个 | dds.slog.<participant_id> | 累积并写入安全日志条目。参见DomainParticipantQos。 |
看门狗(Watchdog) | 文件监控 | 一个 | dds.fwatch | 跟踪被监控文件的修改状态。参见DomainParticipantFactoryQos。 |
回调(Callback) | 文件监控 | 一个 | dds.fwatch.cb | 当被监控文件更改时运行注册的回调。参见DomainParticipantFactoryQos。 |
接收(Reception) | 类型查找服务 | 每个域参与者两个 | dds.tls.replies.<participant_id> 、dds.tls.requests.<participant_id> | 当接收到包含未知数据类型的远程端点发现信息时运行。 |
其中一些线程仅在满足特定条件时才会生成:
- 仅当使用数据共享时,才会创建数据共享监听器线程。
- 仅当域参与者配置为发现服务器的服务器(SERVER)时,才会创建发现服务器事件线程。
- TCP保活线程要求保活周期配置为大于零的值。
- 安全日志记录和共享内存数据包日志记录线程都需要启用某些配置选项。
- 仅当使用FASTDDS_ENVIRONMENT_FILE时,才会生成文件监控线程。
关于传输线程,Fast DDS默认使用UDP和共享内存传输。端口配置可以根据部署的特定需求进行配置,但默认配置是始终使用一个元流量端口和一个单播用户流量端口。这适用于UDP和共享内存,因为TCP不支持多播。更多信息可以在默认监听定位器页面找到。
Fast DDS提供了通过ThreadSettings配置其创建的线程的某些属性的可能性。
3.2 事件驱动架构
存在一个时间-事件系统,使Fast DDS能够响应某些条件以及安排周期性操作。其中很少一部分对用户可见,因为大多数与DDS和RTPS元数据相关。然而,用户可以通过继承TimedEvent
类在其应用程序中定义周期性时间-事件。
四、功能
Fast DDS有一些附加功能,用户可以在其应用程序中实现和配置。下面概述了这些功能。
4.1 发现协议
发现协议定义了在特定主题下发布的DataWriter与订阅同一主题的DataReader进行匹配以便开始共享数据的机制。这适用于通信过程的任何阶段。Fast DDS提供以下发现机制:
- 简单发现(Simple Discovery):这是默认的发现机制,在RTPS标准中定义,提供与其他DDS实现的兼容性。在这里,域参与者在早期阶段被单独发现,随后匹配它们所实现的DataWriter和DataReader。
- 发现服务器(Discovery Server):这种发现机制使用集中式发现架构,其中服务器充当元流量发现的枢纽。
- 静态发现(Static Discovery):这实现了域参与者之间的相互发现,但如果远程域参与者预先知道每个域参与者中包含的实体(DataReader/DataWriter),则可以跳过这些实体的发现。
- 手动发现(Manual Discovery):这种机制仅与RTPS层兼容。它允许用户使用任何外部元信息通道手动匹配和解除匹配RTPS参与者、RTPS写入器和RTPS读取器。
Fast DDS中实现的所有发现协议的详细解释和配置可以在发现部分看到。
4.2 安全性
Fast DDS可以通过在三个级别实现可插拔安全性来配置以提供安全通信:
- 远程域参与者的身份验证:DDS:Auth:PKI-DH插件使用可信证书颁发机构(CA)和ECDSA数字签名算法进行相互身份验证。它还使用椭圆曲线Diffie-Hellman(ECDH)或MODP-2048 Diffie-Hellman(DH)作为密钥协商协议来建立共享密钥。
- 实体的访问控制:DDS:Access:Permissions插件在DDS域和主题级别提供对域参与者的访问控制。
- 数据加密:DDS:Crypto:AES-GCM-GMAC插件使用伽罗瓦计数器模式下的高级加密标准(AES-GCM)提供认证加密。
4.3 日志记录
Fast DDS提供了一个可扩展的日志记录系统。Log
类是日志记录系统的入口点。它公开了三个宏定义以方便使用:EPROSIMA_LOG_INFO
、EPROSIMA_LOG_WARNING
和EPROSIMA_LOG_ERROR
。此外,除了已有的类别(INFO_MSG
、WARN_MSG
和ERROR_MSG
),它还允许定义新的类别。它提供了使用正则表达式按类别进行过滤,以及对日志记录系统的详细程度的控制。日志记录系统可能的配置详情可以在日志记录部分找到。
4.4 XML配置文件
Fast DDS提供了通过使用XML配置文件更改其默认设置的可能性。因此,可以修改DDS实体的行为,而无需用户编写任何程序源代码或重新构建现有应用程序。
用户拥有每个API功能的XML标签。因此,可以通过<participant>
标签构建和配置域参与者配置文件,或者分别通过<data_writer>
和<data_reader>
标签构建和配置DataWriter和DataReader配置文件。
为了更好地理解如何编写和使用这些XML配置文件,您可以继续阅读XML配置文件部分。
4.5 环境变量
环境变量是在程序范围之外通过操作系统功能定义的变量。Fast DDS依靠环境变量,以便用户可以轻松自定义DDS应用程序的默认设置。有关影响Fast DDS的环境变量的完整列表和描述,请参考环境变量部分。