广泛用于航空航天的分布式通信框架——CycloneDDS
简介
CycloneDDS是航空航天中最为常见的分布式通信系统,目前在工作中也会涉及该框架,本文简单介绍了Cyclone DDS的核心API与QoS策略,涵盖参与者创建、主题定义、发布者配置等关键接口,并通过IDL工具展示通信协议生成过程。同时详细探讨数据可用性、交付可靠性等QoS机制及其兼容性规则
基础API介绍
dds_create_participant创建参与者
DDS_EXPORT dds_entity_t
dds_create_participant(
const dds_domainid_t domain,
const dds_qos_t *qos,
const dds_listener_t *listener);
为某一个域创建参与者,domain表示域ID,不同域的参与者在同一物理网络中互不可见(笔者将其理解为虚拟局域网中的VLAN ID);qos表示质量指标,listener为设置的监听
dds_create_topic创建topic
DDS_EXPORT dds_entity_t
dds_create_topic(
dds_entity_t participant,
const dds_topic_descriptor_t *descriptor,
const char *name,
const dds_qos_t *qos,
const dds_listener_t *listener);
为参与者participant创建一个topic,name为topic的名称。这里需要着重介绍的是descriptor,这是通过CycloneDDS的idlc工具创建的
idlc工具
idlc的主要任务就是即将.idl文件转换成目标语言代码,这和Protobuf中的protoc有着异曲同工之妙。.idl中定义了发布者和订阅者之间的通信协议,idlc将会基于此协议自动生成序列化和反序列化的代码,例如笔者定义了如下.idl协议文件
module Chat {
@final
struct Message {
string content; // 消息内容
long sender_id; // 发送者ID
};
};
.idl的语法笔者这里不再介绍。idlc基于这个.idl文件,会生成Chat.h和Chat.c两个文件(默认生成C语言的代码)
typedef struct Chat_Message
{
char * content;
int32_t sender_id;
} Chat_Message;
extern const dds_topic_descriptor_t Chat_Message_desc;
#define Chat_Message__alloc() \
((Chat_Message*) dds_alloc (sizeof (Chat_Message)));
#define Chat_Message_free(d,o) \
dds_sample_free ((d), &Chat_Message_desc, (o))
这是头文件中的主要内容,Chat_Messagestruct是对应的结构体
dds_topic_descriptor_t 用于描述每种通信协议的元信息,这其中包括结构的字段名称、类型、大小、内存对齐方式等,Chat_Message_desc的具体定义在Chat.c中
前面通过dds_create_topic创建topic时,就需要知道这个topic通信协议的元数据,这个导出的Chat_Message_desc就是需要传递给dds_create_topic的对象
Chat_Message__alloc宏和Chat_Message_free宏用于方便的创建和释放通信对象,这里值得注意的是,Chat_Message__alloc只会创建对象本身,对于指针类型,它不会为指针分配内存;所以在使用Chat_Message_free释放对象时尤其需要注意,它有多种释放模式,宏的参数o即表示使用哪种释放模式
DDS_FREE_KEY:只释放对象本身,如果某个字段指向一个堆内存,这块堆内存不会被释放。适用于对象由Chat_Message__alloc创建,但是字段指向的资源需要由外部管理的场景,如地址在栈上,不需要主动释放DDS_FREE_CONTENTS:只释放字段指向的资源,但是不会释放对象本身。适用于对象本身在栈上或者由外部管理,但是字段指向的资源由dds_string_dup或者dds_alloc分配的场景DDS_FREE_ALL:释放这个对象的所有资源,包括对象本身和内部字段指向的资源。适用于对象本身和内部字段指向的资源都是由dds_string_dup或者dds_alloc分配的场景
这是Chat_Message_free释放资源的三种模式
dds_create_publisher创建发布者
DDS_EXPORT dds_entity_t
dds_create_publisher(
dds_entity_t participant,
const dds_qos_t *qos,
const dds_listener_t *listener);
这里的listener可以设置有如下
dds_lset_publication_matched_arg或dds_lset_publication_matched:当有参与者订阅或者取消订阅时,会回调设置的callback,在callback中会有详细的连接信息,例如总连接数、当前连接数、最后一个订阅者的信息;前者可以为callback设置userdata,后者不可以;同时,前者可以设置reset_on_invoke参数,后者默认设置为truedds_lset_offered_incompatible_qos_arg或dds_lset_offered_incompatible_qos:当有参与者订阅,但是QoS策略和发布者不兼容时,callback被回调
QoS策略
在DDS中,QoS用于控制数据的可用性、交付方式、时效性及资源使用等,这是发布者和订阅者之间的一种“契约”,只有当发布者和订阅者之间的QoS策略兼容时,通信才能建立,如果不兼容则会触发设置了相关回调的函数
CycloneDDS中,使用dds_qos_t对QoS进行描述,通过dds_create_qos()创建一个dds_qos_t。QoS的设置主要有以下
数据可用性Durability
数据可用性包括多个方面,对于历史数据是否需要保留的配置通过dds_qset_durability进行配置,选项如下
DDS_DURABILITY_VOLATILE:不保留历史数据DDS_DURABILITY_TRANSIENT_LOCAL:这种类型和紧跟的两种都表示需要保留历史数据,只是保留历史数据的方式不同。其中最常用的就是DDS_DURABILITY_TRANSIENT_LOCAL,表示将历史数据保留在发布者所在进程管理的内存队列中,另外两种则需要额外的独立进程来保存历史数据DDS_DURABILITY_TRANSIENTDDS_DURABILITY_PERSISTENT
如果发布者选择了保留历史数据,那么可以通过dds_qset_durability_service设置保留数据的策略
DDS_HISTORY_KEEP_LAST:保留最新的N条数据DDS_HISTORY_KEEP_ALL:保留所有的历史数据,这会受到到硬件资源限制
数据交付Reliability
数据交付质量通过dds_qset_reliability进行设置
DDS_RELIABILITY_BEST_EFFORT:尽力而为,可能丢包DDS_RELIABILITY_RELIABLE:可靠传输,支持重传
笔者试验下来,如果订阅者想要开始订阅时能够收到一些历史数据,那么需要发布者和订阅者的Reliability都设置为DDS_RELIABILITY_RELIABLE
QoS的兼容性
Reliability中,DDS_RELIABILITY_RELIABLE的能力最强,所以该指标一定兼容订阅者
Durability中,DDS_DURABILITY_VOLATILE的能力最弱,其只能兼容同为DDS_DURABILITY_VOLATILE的订阅者
总结
CycloneDDS中的接口设计有点繁琐,网上的资料也相对较少,笔者正在通过试验的方式继续深入了解该框架中关于事件回调通知、底层通信原理以及消息处理相关的内容,后续将继续更新
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)