翻译 - CycloneDDS 发现行为
本文翻译自 Eclipse Cyclone DDS 11.0.0 官方文档 Discovery behaviour,内容涵盖:代理(Proxy)机制如何约束发现与通信、进程内多参与者如何共享发现信息、写入器滞留(lingering)与 Writer History Cache 的历史保留及限流。适合需要理解 CycloneDDS 发现行为细节、或在做 QoS 与性能调优的读者。译文忠实原文,配置项名称保留英文原样。
代理参与者与端点
在 DDSI 规范中,Eclipse Cyclone DDS 被称为一种有状态(stateful)实现。写入器(writer)仅向已发现的读取器(reader)发送数据,而读取器仅接受来自已发现写入器的数据。存在一个例外:写入器可以选择以多播方式发送数据,从而使任何读取器都能接收。如果读取器已经发现了写入器,但反之亦然的情况尚未发生,即使连接未完全建立,该读取器仍可接受数据。
这种不对称的发现机制可能导致数据在预期之外被交付,甚至引发无限期阻塞。为避免此类问题,Eclipse Cyclone DDS 会在内部为每个远程参与者(participant)、读取器和写入器创建代理(proxy)。在发现过程中,写入器会与代理读取器进行匹配,读取器会与代理写入器进行匹配,匹配依据为主题名称(topic name)、类型名称(type name)以及 QoS 设置。
代理具有与"普通"DDSI 实体相同的自然层级结构。每个代理端点(proxy endpoint)均归属于一个代理参与者(proxy participant)。当代理参与者被删除时,其所有代理端点也会随之删除。参与者会定期声明其存活状态,这在 DCPS 规范中称为自动存活(automatic liveliness),也是 Eclipse Cyclone DDS 唯一支持的模式。如果在租约持续时间(lease duration,由该参与者在其 SPDP 消息中发布)内未收到来自某参与者的任何消息,则租约过期,触发清理操作。
删除端点会在 SEDP 协议中触发"dispose"和"un-register"操作。删除参与者还会生成特殊消息,使对等方能够立即回收资源,而无需等待租约过期。
发现信息共享
Eclipse Cyclone DDS 以集成方式处理任意数量的参与者,但"代理参与者与端点"一节中描述的发现协议可能造成资源浪费。Eclipse Cyclone DDS 进程中的每个参与者无需各自独立运行完整的发现协议。
Eclipse Cyclone DDS 并未完全按照 DDSI 规范建议的方式实现该协议,而是让所有参与者共享发现活动,从而允许您向进程中添加参与者时对系统影响最小化。
此外,无论应用代码创建了多少个 DCPS 参与者,进程中都可以只存在一个 DDSI 参与者,该参与者将成为该进程中创建的所有端点的虚拟所有者。拥有大量参与者不会产生发现开销,但可能会影响基于参与者的存活监控。
由于其他 DDSI 规范实现可能假设所有参与者都会独立执行发现过程,Eclipse Cyclone DDS 可以模拟这种行为。它并不会为每个参与者独立执行发现,但会生成仿佛如此的网络流量。这些行为可通过 Internal/BuiltinEndpointSet 选项进行控制。
通过在单个节点的所有参与者之间共享发现信息,每个新加入的参与者或端点可以立即感知现有的对等方,并直接与其通信。如果这些对等方需要较长时间才能发现新的参与者或端点,则可能产生一些冗余的网络流量。
滞留写入器(Lingering Writers)
当应用程序删除一个可靠的 DCPS 写入器时,无法保证所有读取器都已确认正确接收到所有样本。Eclipse Cyclone DDS 允许该写入器(必要时包括其所属参与者)在系统中暂时滞留一段时间,具体时长由 Internal/writerLingerDuration 选项控制。当所有读取器均已确认所有样本,或滞留时间到期(以先发生者为准)时,写入器将被删除。
注意
当请求终止 Eclipse Cyclone DDS 时,不会应用写入器滞留时长设置。
写入器历史 QoS 与限流
DDSI 规范依赖于写入器历史缓存(Writer History Cache, WHC),其中每个样本由一个序列号唯一标识。WHC 对写入器发布的样本集成了两种不同的索引:
- 序列号索引:用于重传丢失的样本,因此对所有可靠写入器都是必需的(参见"可靠通信")。
- 键值索引(key value index):用于在 WHC 中保留每个实例的当前状态。
当新样本覆盖某个实例的状态时,键值索引允许从序列号索引中丢弃旧样本。对于瞬态本地行为(transient-local behaviour,参见"DDSI 特定的瞬态本地行为"),键值索引还允许在所有读取器确认样本后,仍保留每个实例的当前状态。
瞬态本地数据始终需要键值索引,默认情况下,其他历史设置为 KEEP_LAST 的写入器也会使用该索引。键值索引的优势在于可以积极地丢弃已被覆盖的样本,而不是将其交付给所有读取器。其缺点是资源消耗略高。
WHC 区分以下两类历史记录:
- 为现有读取器保留的历史(由写入器的 history QoS 设置控制)。
- 为迟加入的读取器保留的历史(针对瞬态本地写入器,由主题的 durability-service history QoS 设置控制)。
因此,可以创建一个写入器,它对活跃读取器永不覆盖样本,同时仅为迟加入的读取器保留最新样本。这确保了迟加入读取器所能获取的数据,在瞬态本地数据和瞬态数据之间保持一致。
写入器限流(Writer Throttling)
写入器限流基于 WHC 的大小。以下设置用于控制写入器限流:
- 当 WHC 中未确认样本的字节数达到或超过
high值时,写入器将被暂停,直到未确认样本的字节数降至Internal/Watermarks/WhcLow所设定的值以下。 - 根据传输压力和接收端的重传请求,
high的值会在以下两个阈值之间动态调整:Internal/Watermarks/WhcLowInternal/Watermarks/WhcHigh
high的初始值由Internal/Watermarks/WhcHighInit设定。- 可通过将
Internal/Watermarks/WhcAdaptive设置为false来禁用自适应行为。
虽然自适应行为通常能很好地处理各种速度快慢不一的写入器和读取器,但在一个高速传输数据的现有网络中,若引入一个缓冲区很小且速度极慢的新读取器,可能会导致系统突然停滞。这是因为新读取器试图恢复写入器中存储的大量数据,之后整个系统只能以更低的速率继续运行。
相关阅读:代理机制的图解展开见《Cyclone DDS 代理模型:发现协议与状态镜像》,数据收发路径见《翻译 - 数据路径配置》。