基于发布订阅的进程间通信设计与实现*
2020-07-22刘晨璐乔磊彭飞徐建曹海宁
刘晨璐,乔磊,彭飞,徐建,曹海宁
0 引 言
随着航天器应用范围的扩大,航天器不再是一个单一的个体,而是和其他航天器共同构建形成网络.每个航天器不仅需要处理自身采集的多样的环境信息,还需与其他航天器进行信息交互.以卫星为例,若干个在轨的孤立卫星可以编制成为纵横交错的卫星网络.由于每类传感器获取的信息有限,如果单独处理的话,就会割裂和其他星上传感器间的联系,造成信息资源的浪费.将多颗卫星组成网络,有利于综合不同卫星的感应信息,通过信息间的互补融合,做出更加精准的判断.因此,如何建立通信机制传递多星或者星内的数据,成为了研究学者们亟待解决的问题.
针对该问题,国外航天领域开展了深入的研究,NASA提出了CFS软件架构作为软件协同接口,通过软件总线进行数据通信,并提供消息传递接口.国际标准化组织OMG制定发布DDS(data distribution service)通信协议,该协议简化网络编程,并提供标准的数据传输接口.另外,为了满足实时系统的需要,DDS还提供了不同的服务质量(QoS)选项,保证UDP状态下的可靠传输.美国作为该领域的领跑者,已将DDS广泛应用于航空、航天、船舶、国防等领域.我国在该领域起步较晚,从2008年开始引进相关成熟技术应用在商业中,效果显著.但是由于当前核心技术均依赖于其他国家,无法安全地将其应用在国防领域.
航天器传感器消息的传递能够简化成单机或者多机进程间的通信问题,传统的通信方式主要有消息传递、远程过程调用(RPC,remote procedure call)和共享空间3种.消息传递是指线程或进程之间的直接通信,一般先将消息放置在消息队列中,这样其他线程或进程可以在任何时候取得属于自己的消息.RPC是指远程过程调用,例如两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法.由于两台服务器的开发语言可能不同,通常将两端的数据序列化后,再借助网络进行数据传递,拿到数据后再进行反序列化的操作.与消息传递不同的是,RPC属于同步通信,客户方需等待服务端调用执行完成并返回结果.共享内存方式是指两个或多个线程之间通过读写内存中的公共状态来隐式进行通信,一般用在一个进程中的多个线程间的消息交互.
表1 4种通信机制比较Tab.1 Comparisons of four communication Mechanisms
1987年,在计算机协会(ACM)的操作系统原理的研讨会上,Frank Schmuc在论文中描述了发布订阅技术.发布订阅是一种消息分发的控制机制,通常将发布消息的一端称为发布者,接收消息的一端称为订阅者.与传统的通信方式相比,发布订阅的特点是消息转发的目的地由消息的接收者动态控制[1],主要分为基于主题的发布订阅和基于内容的发布订阅.
表2 发布订阅机制Tab.2 Publish subscribe mechanism
基于主题的发布订阅[2-3]将消息按照主题划分成组,每个消息只属于其中一个主题.订阅者在订阅信息时,需要指明对哪个主题感兴趣,将来一旦出现该主题下的消息,系统都会自动将其发送给订阅者,订阅者接收其订阅主题的所有消息.
在基于内容的发布订阅[4-8]中,订阅者自由约束条件来表达他们的兴趣,并不局限在特定的主题.订阅者在订阅前不需要知道一套主题名,系统通过匹配算法来分发消息.
如表2所示,由于基于内容的发布订阅需根据订阅条件匹配消息,给系统带来很大的负担.本文研究的重点主要是在基于主题的发布订阅.
2010年Willow Garage公司发布了开源机器人操作系统ROS,该系统的发布订阅机制主要有三种:基于主题的异步数据流通信、基于服务的同步RPC通信以及基于参数服务器的数据传递[9-10].由于ROS的源码是用C++编写的,无法直接应用在嵌入式平台.因此,本文利用C语言实现ROS中进程间的通信功能,方便嵌入式平台的使用,为航天器信息传输提供一种选择,同时可扩展应用在无人装备、指挥调度网络中等.
本文第一章将从基于主题的发布订阅机制入手,介绍该机制各部分的功能及优点.第二章重点讲述了网络依赖的通信机制以及相关的数据结构.第三章是实验部分,对该架构进行了合理性的验证.最后一章对该发布订阅机制进行了分析和总结,明确了下一步的研究方向.
1 基于主题的发布订阅机制
发布订阅机制以节点为基本单位,节点之间通过主题建立连接,只有关注同一主题的节点才能进行通信.另外,该机制用节点管理器对节点进行统一管理,任何节点上线前都会在节点管理器上进行注册.图1是网络具体的通信流程,将节点设置成发布者或者订阅者,向Core申请加入网络.申请成功后,订阅者根据Core返回的信息,与发布者建立联系,开始消息的传递.
1.1 概念介绍
由于节点、主题和节点管理器是通信网络中必不可少的组成部分,以下将对各部分进行详细的介绍.
发布订阅机制将可执行程序虚拟成节点运行,一个通信网络可以由很多节点组成,每个节点有若干个感兴趣的话题,并且可以随意设置为这些话题的发布端或者订阅端.此外,每个节点拥有唯一的URL,以便和其他节点进行通信.节点建立后首先向节点管理器进行注册,申请加入整个通信网络,之后通过主题和其他节点建立链接,从而开始消息的发布和订阅.
图1 网络通信流程Fig.1 Network communication flow
主题是整个消息传播的纽带,节点通过订阅其他节点发布的主题,才能接收该主题的消息.在订阅主题过程中,节点只需要知道主题的信息,而不需要了解发布该主题的节点信息,这就保证了发布端和订阅端的功能解耦.值得注意的是,主题的命名具有唯一性,整个网络可以有若干个话题,但是节点之间只有主题相匹配,才能进行该主题消息的传递.
节点管理器充当的是“管家”的作用,每一个节点要想加入网络,都需先和管家进行沟通,告诉管家自己感兴趣的话题以及所承担的角色(订阅端或者发布端).管家在记录信息后,会通知其他相关的节点,这就完成了注册的过程.整个网络结构中,只有管家知道所有节点的信息,发布端和订阅端都不需要知道彼此的信息.
1.3 机制优点
发布订阅的通信机制主要有以下3个优点:
1)发布端和订阅端功能解耦,发布者和订阅者不需要知道系统的拓扑结构就可以正常的工作,从而提高了系统的可维护性.
2)扩展性较强,整个网络结构非常灵活,具有良好的伸缩性,既可以简单的编写一两个节点单独运行,也可将节点组织成很大的网络.其中任何一个节点都可以设置成某话题的发布端或订阅端.
3)接口简单,整个架构将程序虚拟化成节点运行,通过主题将所有节点构成网络,再进行数据的发布以及节点间的数据交互.任何两点间需要通信,只需关注相同的主题,再调用接口建立联系即可.
2 发布订阅机制设计
2.1 通信机制设计
节点是运行的实体,管理器(core)、发布端(pub)和订阅端(sub)属于节点的某种属性,其中管理器主要用于统一管理网络拓扑中存在的节点;发布端主要负责特征主题消息的发布;订阅端则是接收相关主题的消息.
整个通信机制如图2所示,发布端初始化完成后,首先按照一定的消息格式向管理器发送消息,告诉管理器自身关注的话题内容、角色信息(订阅者或者发布者)、IP地址以及端口号,申请加入整个通信网络.管理器根据新上线节点的信息,更新自身话题的发布链表,并通知网络中同话题的订阅者,使订阅端得到最新的发布者链表,至此发布端注册完成.
图2 数据流传递过程Fig.2 Data flow transfer process
发布端注册成功后,可以断开与管理器的连接,开始消息的发送.一方面,网络中不断会有新的订阅端和该节点建立链接,此时发布端会更新该话题订阅者的信息,记录下订阅端的套接字内容.另一方面,一旦有新的消息要发布,发布端开始遍历自身节点,找到对应话题的订阅端套接字,直接发送消息内容,从而完成了消息的发布工作.
当一个订阅端进行简单的初始化之后,也会向管理器进行注册,发送的内容和发布端的一致,包括自身关注的话题内容、角色信息(订阅者或者发布者)、IP地址以及端口号.和发布端不同的是,订阅端始终和管理器保持沟通,不断接收管理器发来的消息.这些消息都是同话题发布端的信息,主要包括IP地址和端口号.订阅端收到这些消息后,会筛选出尚未建立连接的发布端,并逐个建立连接.与此同时,订阅端一直在接收同话题发布端的消息,只要有消息就会触发订阅端的callback函数,对消息进行及时的处理.
2.2 数据结构设计
数据结构的设计是整个工程的难点,一个好的数据结构可以大量降低代码开发的难度,在图3中列出了整体的数据结构.由于这些部分功能不同,我们对其进行单独的数据结构设计,既能使得各部分功能单一,又可以方便接口的调用,利用工程的实现.
节点是该通信机制中运行的基本单元,主要记录了自身感兴趣的话题内容.从数据结构上来说,一个节点是包含很多话题的数组,即图3中的topics.从功能上来说,节点拥有自身的全部信息,可以通过对节点内部话题的遍利,得知该节点在哪些话题中承担发布者的角色,又在订阅哪些话题发布的消息.另外在通信过程中,每个节点都有自己唯一的IP地址,以便和其他节点信息交互.
图3 数据结构Fig.3 Data structure
主题结构的设计是整个问题的核心部分,主题首先包含一些自身的信息,如:该主题所属的节点信息(node),该主题的名称(name)等.另外,该主题也会存储一些相关的信息,如:该主题的订阅端信息,该主题的发布端信息等.值得注意的是,如果该节点属于这个主题的发布端,那么在主题结构中,只存储该主题的订阅端.同样的,如果该节点是订阅端,也只存储该话题的发布端即可.
那么订阅端和发布端的信息如何存储呢?从图3中,发现主题的数据结构中,除了存储订阅端(subscriber)和发布端(publisher)自身信息的指针外,还需存储在实际通信过程产生的套接字信息(linksoc).这样发布端发布消息时,只需遍历自身节点,寻找该话题下对应的订阅端套接字信息即可.
发布端的数据结构中存储两方面的内容,一是自身唯一的端口号(port)和所属话题信息(topic),二是会触发消息发送的消息队列(msg_link).
订阅端的数据结构和发布端的类似,不同的是订阅端存储了多个队列用于建立新的连接.其中一个队列记录自身已经建立的连接,另外一个存储还未建立连接的发布端信息.
3 实验验证
和网络中的其他节点不同的是,管理器记录了所有节点的信息,可以将管理器部署在网络中的任一单机上.由于每个节点都有自己特定的功能,在实际应用中,将节点视为一个进程或者线程来运行,进行发布订阅功能的测试.
3.1 实验环境
本算法在windows平台上实现,底层主要依赖的是socket通信机制[11-15],采用C语言进行开发,建立发布端和订阅端的直连,通过TPC协议完成3次握手,便于后续嵌入式平台的移植.
3.2 实验结果
本文的通信机制适用于进程间的交互,在单机和多机上进行功能验证.为了方便区别订阅端接收的消息,发布端默认发布的是自己的端口号(一般情况下端口号不会冲突).首先启动core进程,再随机建立任意个数的同话题的发布端和订阅端,无论创建的顺序如何,订阅端可以接收同一话题的所有发布端的消息.
如图4所示,在单机上进行测试,先建立一个发布端pub和订阅端sub,又创建了同话题的发布端pub.每次新建节点的时候,core都会收到消息,例如“1+topic1+127.30.94.1#1493#”,其中1代表该节点是发布端,topic1是该节点关注的主题,后面的两个分别是该节点的IP地址和端口号.注册完成后,发布端/订阅端就在等待订阅端/发布端的出现.当有同话题的订阅端注册成功后时,发布端会将自身的端口号作为消息发送给订阅端,由于进程的分时机制,订阅端会不定时地收到来自两个发布端的消息,即“1493”和“1599”两个端口号,通过和core收到的消息进行比对,与发布端的端口号一致.另外,也在多机上进行了测试,可以实现多机发布订阅的功能,实验效果图类似,这里就不再赘述了.
4 结 论
本文利用基于主题的发布订阅机制,实现了进程间消息的传递.该机制以节点为运行单位,利用主题建立节点间的网络连接关系,实现订阅端和发布端的消息交互,为航天器提供了一种点对点的通信模式.
由于节点管理器属于中心节点,一旦中心节点有问题,新的节点将不能加入,只能维持原有网络的运行.因此,下一步研究的重点主要是在节点管理器去中心化上,可以多机部署虚拟节点管理器,一旦某个节点管理器有问题,其他的节点管理器可以及时替代,从而减少注册瘫痪的可能性.