4. QoS策略(上):可靠性、持久性、历史记录

各位同学,今天我们聊聊DDS里最核心的三个QoS策略。说实话,这三个策略要是搞不明白,后面做项目肯定要踩坑。我自己带团队的时候,发现80%的通信问题都出在这三个策略的配置上。

4.1 可靠性(RELIABILITY)——消息到底能不能丢?

先问大家一个问题:你的系统允许丢消息吗?

如果答案是「绝对不能丢」,那你就得用 RELIABLE 模式。如果偶尔丢几条无所谓,那就用 BEST_EFFORT

我个人习惯把可靠性策略比作快递服务:

  • BEST_EFFORT 就像普通快递——丢了不赔,但速度快
  • RELIABLE 就像顺丰保价——必须送到,但可能慢一点

我在项目中遇到过这样一个案例:一个自动驾驶的感知模块,用BEST_EFFORT发障碍物信息。结果有一次丢包了,控制模块没收到障碍物数据,差点出事。嗯,从那以后,所有安全相关的topic我都强制用RELIABLE。

核心要点:

  • RELIABLE:发送方会重传,直到接收方确认收到
  • BEST_EFFORT:发完就不管了,适合传感器数据、日志等

代码配置示例:

// 创建QoS对象
QosPolicy qos = QosPolicy.createDefault();

// 设置为可靠模式
qos.setReliability(ReliabilityKind.RELIABLE);

// 或者设置为尽力而为
qos.setReliability(ReliabilityKind.BEST_EFFORT);

注意:RELIABLE模式会占用更多内存和带宽。我曾经有个项目,所有topic都用RELIABLE,结果内存爆了。后来把高频传感器数据改成BEST_EFFORT,问题就解决了。

4.2 持久性(DURABILITY)——晚来的订阅者怎么办?

这个策略解决的是「后来者」的问题。你想想看,一个订阅者晚到了,它还能不能看到之前发布的数据?

持久性策略有四种:

策略 含义 适用场景
VOLATILE 不保存历史数据 实时数据,如传感器值
TRANSIENT_LOCAL 保存在发布者内存中 配置参数、静态信息
TRANSIENT 保存在持久化服务中 需要跨进程共享的数据
PERSISTENT 保存在磁盘上 关键配置,重启后仍需保留

我建议你记住一个原则:持久性越强,代价越大。PERSISTENT虽然最安全,但读写磁盘很慢。我在做车载诊断系统时,就把故障码用PERSISTENT存着,这样车重启后还能查到历史故障。

我的经验:大多数场景用TRANSIENT_LOCAL就够了。只有需要跨ECU持久化的数据,才考虑TRANSIENT或PERSISTENT。

4.3 历史记录(HISTORY)——保留多少条旧数据?

历史记录策略和持久性策略是搭配使用的。它决定了「要保留多少条历史数据」。

有两种模式:

  • KEEP_LAST:只保留最近N条数据
  • KEEP_ALL:保留所有数据,直到资源耗尽

代码示例:

// 保留最近10条数据
qos.setHistory(HistoryKind.KEEP_LAST, 10);

// 保留所有数据
qos.setHistory(HistoryKind.KEEP_ALL);

避坑指南:我曾经用KEEP_ALL配合RELIABLE,结果数据量太大,内存直接爆了。后来改成KEEP_LAST + depth=100,问题就解决了。记住:KEEP_ALL要慎用,除非你确定数据量很小。

4.4 三个策略的联动关系

这三个策略不是孤立的,它们会互相影响。我画了一张图来说明:

可靠性 RELIABILITY 持久性 DURABILITY 历史记录 HISTORY 决定消息是否重传 决定历史数据是否保留 决定保留多少条 联动关系 ● RELIABLE + KEEP_LAST: 重传失败的消息,保留最近N条历史 ● TRANSIENT_LOCAL + KEEP_ALL: 保留所有历史数据,但注意内存限制

说白了,这三个策略的配置要一起考虑。我一般这样配:

  • 实时控制信号:RELIABLE + VOLATILE + KEEP_LAST(1)
  • 传感器数据:BEST_EFFORT + VOLATILE + KEEP_LAST(10)
  • 配置参数:RELIABLE + TRANSIENT_LOCAL + KEEP_LAST(5)

总结一下:

  • 可靠性:保证消息送达
  • 持久性:保证后来者能看到历史
  • 历史记录:控制保留多少历史

这三个策略是DDS的基石。搞懂了它们,你就掌握了DDS通信的核心。

专注资料整理