2. DDS 基础概念

DDS(Data Distribution Service)是面向实时分布式系统的数据通信中间件标准。它不采用“客户端请求—服务器响应”的中心化模式,而是围绕以数据为中心的发布订阅模型构建。理解 DDS 的五个基础概念——发布订阅模型、域与分区、主题与实例、数据写入者与数据读取者、全局数据空间——是后续掌握 QoS 与可靠通信工程的前提。

一、发布订阅模型:以数据为中心的通信范式

在 DDS 中,通信双方不直接建立连接,而是围绕“数据对象”进行解耦。发布者(Publisher)将数据写入某个主题,订阅者(Subscriber)声明自己关心某个主题,中间件负责按需匹配并投递。这种模型带来三个关键解耦:

  • 空间解耦:发布者与订阅者无需知道对方的网络地址或进程标识。
  • 时间解耦:双方不必同时在线,DDS 可通过历史缓存与持久化支持迟到订阅者。
  • 流解耦:同一份数据可被任意数量的订阅者同时消费,发布者无需为每个消费者单独发送。

与消息队列不同,DDS 的发布订阅是“数据状态”的传播,而非“消息事件”的排队。订阅者最终关心的是某个主题下数据的最新值或历史序列,而不是消息被消费的次数。

核心思想:发布者写“数据”,订阅者读“数据”,中间件根据主题、类型、QoS 自动完成匹配与投递。

二、域与分区:逻辑隔离的两级手段

域(Domain)是 DDS 中最大的逻辑隔离单位。每个域由一个 domainId 标识,不同域之间的实体默认完全不可见,即使运行在同一台机器上。域通常用于隔离不同车辆、不同功能集群或不同安全等级的系统。

分区(Partition)是域内的进一步逻辑划分。发布者和订阅者通过分区名集合进行匹配:只有分区名存在交集时,数据才可能被投递。分区常用于同一域内按功能、按区域或按优先级隔离数据流,例如将“感知数据”和“控制指令”放在不同分区中。

隔离手段 作用范围 匹配依据 典型用途
域 Domain 全局最大隔离 domainId 不同车型、不同安全域
分区 Partition 域内逻辑隔离 分区名集合交集 感知/控制/诊断数据分流
主题 Topic 数据类型与名称匹配 主题名 + 数据类型 同类数据的发布订阅

三、主题与实例:数据组织的两个维度

主题(Topic)是 DDS 中数据发布订阅的基本单位,由“主题名 + 数据类型”共同定义。主题名是字符串标识,数据类型是 IDL 定义的结构体。只有主题名和数据类型都匹配的写入者与读取者才能通信。

实例(Instance)是主题内部的数据分片。一个主题可以包含多个实例,每个实例由数据中的键(Key)字段唯一标识。例如主题“VehicleStatus”以 vehicleId 为键,则每辆车对应一个实例。实例机制让 DDS 能够对同一主题下的不同对象分别维护状态、生命周期和历史缓存。

struct VehicleStatus {
    @key long vehicleId;   // 键字段,决定实例
    double speed;
    double battery;
    long   timestamp;
};
// vehicleId=101 与 vehicleId=102 属于同一主题的不同实例

实例状态包括 ALIVE、NOT_ALIVE_DISPOSED、NOT_ALIVE_NO_WRITERS 等,读取者可以感知某个实例是被显式销毁,还是因为写入者消失而失效。这是 DDS 区别于普通消息中间件的重要能力。

四、数据写入者与数据读取者:通信端点的核心角色

在 DDS 实体层次中,Publisher 管理 DataWriter,Subscriber 管理 DataReader。真正执行数据写入和读取的是 DataWriter 和 DataReader。

  • DataWriter:绑定到一个主题,负责将数据样本写入全局数据空间,并携带 QoS 策略控制可靠性、历史、 deadline 等行为。
  • DataReader:绑定到一个主题,负责从全局数据空间接收数据样本,并向应用层提供缓存、状态和实例信息。

DataWriter 与 DataReader 之间通过匹配建立关联。匹配条件包括:同一域、主题名一致、数据类型一致、分区名有交集、QoS 兼容。只有匹配成功后,数据才可能被投递。

DataWriter 和 DataReader 是 QoS 策略的实际承载者。后续章节中讨论的可靠性、持久性、 deadline、资源限制等,最终都作用在这两个端点上。

五、全局数据空间:逻辑上的共享数据池

DDS 将整个域抽象为一个全局数据空间(Global Data Space)。发布者向其中写入数据,订阅者从中读取数据,中间件负责维护这个逻辑空间的一致性。全局数据空间不是一块物理内存,而是一个逻辑视图:每个主题对应空间中的一个“数据通道”,每个实例对应通道中的一个“数据槽”。

全局数据空间的价值在于:它把“谁发给谁”的通信问题,转化为“谁写入了什么、谁读取了什么”的数据问题。这使得系统更容易扩展、替换和动态重组。

DDS 全局数据空间与发布订阅关系 域 Domain (domainId = 0) 发布端 Publisher DataWriter A Topic: VehicleStatus DataWriter B Topic: ControlCmd DataWriter C Topic: DiagInfo 全局数据空间 Topic: VehicleStatus 实例: vehicleId=101, 102, 103 Topic: ControlCmd 实例: cmdId=steer, brake Topic: DiagInfo 实例: ecuId=VCU, BMS 订阅端 Subscriber DataReader X Topic: VehicleStatus DataReader Y Topic: ControlCmd DataReader Z Topic: DiagInfo 分区 Partition: "perception" / "control" 分区名有交集才可匹配投递 写入路径 读取路径 分区隔离

六、案例与场景:车载系统中的典型映射

在一辆智能网联汽车中,可以这样映射 DDS 基础概念:

  • 域:动力域使用 domainId=0,智驾域使用 domainId=1,两者默认隔离。
  • 分区:智驾域内,感知数据放入 partition “perception”,控制指令放入 partition “control”。
  • 主题:VehicleStatus、ObstacleList、ControlCmd 等。
  • 实例:ObstacleList 以 obstacleId 为键,每个障碍物是一个实例。
  • DataWriter/DataReader:感知模块创建 DataWriter 写 ObstacleList,规划模块创建 DataReader 读 ObstacleList。
  • 全局数据空间:所有模块通过域内全局数据空间交换数据,无需点对点连接。

七、常见误区与复盘

误区一:把域和分区混为一谈。域是全局隔离,跨域默认不可见;分区是域内逻辑隔离,跨分区仍可能通过其他分区名匹配。二者层级不同。
误区二:认为主题名相同就能通信。主题由“名称 + 数据类型”共同定义,类型不一致即使名称相同也无法匹配。
误区三:忽略实例键的作用。没有键字段时,整个主题视为单个实例;有键字段时,DDS 会按实例分别维护状态和历史,影响资源与 QoS 行为。

复盘建议:在设计阶段先画清“域—分区—主题—实例”四级结构,再确定 DataWriter/DataReader 的部署位置和 QoS 需求。基础概念清晰,后续的可靠性、持久性、 deadline 和资源限制才有稳定的落点。