百度Apollo系统学习-Cyber RT 通信-服务发现
前言
在这一章开始前,默认读者已经读完了专栏中有关cyberRT的注册启动以及通信部分的内容,在通信部分的最后我们遗留了一个问题,那就是在Reader
和Writer
初始化的时候会调用到一个JoinTheTopology
函数,这个函数具体在做什么事情,也是我们这一章主要回答的问题。
基本概念
服务发现模块不负责具体消息的传递,而主要负责监测cyber中通信节点的存活情况,并且提醒已有节点有新节点加入并作出相应动作(比如某个channel多了一个读者,那么对应的写者就需要激活合适的Transmitter
)。
Node
是Server/Client/Writer/Reader
的容器,Server/Client/Writer/Reader
(注意Server
在代码里其实是Service
类,下面提到的Service
是概念Service而不是代码里的Service
)是有向图的顶点,Channel
是Writer
到Reader
的边缘,Service
是Server
到Client
的边缘。cyber中把Writer
和Server
称作Upstream
,另外两个是Downstream
。
TopologyManager
整个服务发现系统是一个全局的管理体系,每个process都有一个单例TopologyManager
(cyber/service_discovery/topology_manager.h
),提供三个拓扑网的管理器NodeManager
,ChannelManager
和ServiceManager
。所有的Node
在NodeManager
中被管理;所有的Channel
以及Reader
和Writer
都在ChannelManager
里;所有的Services
以及Server
和Client
都在ServiceManager
。目前从代码上看,其实TopologyManager
的作用是监视网络中是否有参与者Participant
退出或意外退出,而新参与者的加入是下属的三个管理器负责通知的。
TopologyManager
里的消息是通过通信模式中的RTPS模式来进行通信的,也就是说通信系统实际上不依赖于服务发现系统,服务发现系统实际上是一个更高层面的管理。- 成员有一个RTPS中的参与者,在
TopologyManager::Init()
时会被创建,参与者的名字(不光是TopologyManager
,整个服务发现网络的所有参与者名字都是这样组成的)由全局变量中的HostName()
和ProcessId()
组成,监听的端口是11511(组件通过RTPS模式通信时的名字组成和这个一样,但端口是11512,具体见专栏里通信底层介绍的博客)。随之创建的还有该参与者的监听器(ParticipantListener
,底层由fast-rtps
实现)在该参与者发生变化的时候会调用,这是什么意思呢,其实就是TopologyManager
本身也是这个网络中的一个一个参与者,只不过它负责监视并管理整个网络中的变化。 - 另一个关键成员是
participant_names_
map记录了该拓扑中的其他参与者,在网络中有新的参与者加入或者有参与者退出时就会更新这个map。 TopologyManager::Convert
函数主要是一个功能型的函数,它将fastrtps实现中的ParticipantDiscoveryInfo
也就是参与者的变动信息重新包装成cyber中所需要的信息ChangeMsg
,然后调用自己的信号成员回调之前注册的槽函数(请注意,目前在官方的代码中并没有发现TopologyManager
有注册什么自己的回调函数,也就是说它的成员信号实际上是没用的,但用户可以根据需求注册槽,然后对服务发现网络的变动做出反应)。TopologyManager
的成员参与者participant_
的监听器participant_listener_
绑定了TopologyManager::OnParticipantChange
函数,这个函数在有参与者离开时会通知三个拓扑网管理器这个消息(通过调用XxxManager::OnTopoModuleLeave()
),然后发送信号调用注册的槽函数。这个监听器是在初始化时创建参与者TopologyManager::CreateParticipant()
的时候绑定的,也就是说参与者成员每收到一条从底层的rtps发来的消息就会调用它。
三张拓扑网
Manager
Manager
(在cyber/service_discovery/specific_manager/manager.h
)是三张拓扑网的基类(非TopologyManager
的基类),每个具体的Manager
都是在TopologyManager
创建的时候在TopologyManager::Init()
中被创建并初始化的。Manager
的成员主要就是一个fastrtps的publisher_
和一个subscriber_
以及订阅者对应的回调函数listener_
。这个listener_
是在Manager::CreateSubscriber()
时绑定了Manager::OnRemoteChange()
(回调函数会调用XxxManager::Dispose()
来对这些消息进行处理),而每当有新的可供发现的东西加入或离开拓扑时(比如Node
,Reader
等等)会调用Manager::Join()
或Manager::Leave()
,这两个函数会通过RTPS底层库publish相应的消息,然后订阅者的回调函数listener_
就会调用了。
当然它也和TopologyManager
一样有一个信号。这个信号就是用来在Manager::Notify()
时发送的,而注册槽的函数是Manager::AddChangeListener()
,在Channel网络加入Reader
和Writer
会调用到这个注册函数,具体见Channel网解析。
Node网
管理node拓扑,在cyber/node/node_channel_impl.h
中(Node
被创建或销毁的时候)进行Join & Leave
操作。这个网络主要是监察所有节点的存活,并提供查询接口。
Node
Node
是通信网络中的基本单元,一个组件ComponentBase
都会在初始化XxxComponent::Initialize()
的时候创建一个只属于自己的Node
(也可以通过cyber::CreateNode()
创建),也就是说Node
网中的每一个单元都是一个组件。
Node
有自己的node_name
和name_space
,node_name
来自于不同模块的配置文件.dag
中的不同组件的config.name
,name_space
好像还没见哪里用到过TODO
NodeXxxImpl
作为Node
中两个成员,它们会在Node
被创建的时候一并创建,也就是说,每个Node
负责channel类型的通信节点(Reader
,Writer
),也负责service类型的通信节点(service
,client
)。而在这两个成员被创建时,如果当前是现实环境(is_reality_mode_
),那么NodeChannelImpl
就会调用service_discovery::NodeManager::Join()
将当前节点加入拓扑,NodeServiceImpl
则不会调用而是在创建Client&Service
时才会调用ServiceManager::Join
(非Node网络)。
NodeManager
多了一个NodeWarehouse nodes_
的成员(NodeWarehouse
那一套东西其实就是个线程安全的更高级的map),加入和离开拓扑即往nodes_
中Add
或Remove
节点。NodeManager::Dispose()
函数会根据消息种类来向nodes_
中加入或删除节点,然后调用Notify()
调用等待该信号的槽回调函数。
Channel网
管理Reader
和Writer
的channel拓扑。在Reader
和Writer
初始化时会加入拓扑(调用JoinTheTopology()
),在它们被关掉的时候(Reader,Writer::Shutdown()
调用的LeaveTheTopology()
)则离开拓扑。
JoinTheTopology()
函数会先把Reader & Writer::OnChannelChange()
注册到ChannelManager
的槽中,这样当Channel网拓扑发生变化时就能调用回调函数OnChannelChange()
了。然后对于写者,会去查找该Channel已有的所有读者并为它们Enable
写者自己的的transmitter_
;对于读者则会去查看该Channel已有的写者并为它们Enable
读者自己的receiver_
。最后把自己加入Channel网络拓扑中,加入引起拓扑变化后,该拓扑中对应channel的节点的回调函数都会被调用,写者加入对应读者的receiver
被Enable,读者加入对应写者的transmitter
被Enable。所以每次有读者或写者加入,所有涉及到的transmitter
和receiver
都会主动或被动地被Enable。
Reader::OnChannelChange()
当发现有新的对应channel的读者加入时会调用自己receiver_::Enable(writer_attr)
来注册一些回调函数,这样后面收到消息就能做出反应,具体见通信模块底层介绍的博客;Writer::OnChannelChange()
当发现有新的Reader
订阅自己这个channel时,会调用自己的transmitter_->Enable(reader_attr)
(这里的reader_attr
只有在HybridTransmitter
才会用到,但因为默认就是Hybrid模式所以都会用到)来启用数据写入,如果已经启动过了那就略过。这里需要注意一点,那就是默认的传输方式是Hybrid,所以HybridTransmitter::Enable(opposite_attr)
会根据参数来Enable最合适的Transmitter
,也就是说当有新的读者加入拓扑网络时,相应channel的写者会去根据两者的关系启用最合适的Transmitter
来发送消息。
ChannelManager
提供了新的成员来记录节点间的关系及节点网络的图结构,并分别以node_id
和channel_id
为key来保存读写者。和NodeManager
的其他过程类似,只不过保存的信息和索引方式不同。
Service网
主要是监测service节点的存活并提供查询接口。
Service & Client
在通信模块上层结构中,Node
存在有4种不同的基本单元:Writer, Reader, Service(即Server), Client
。Writer
拥有底层的Transmitter
,Reader
拥有底层的Receiver
,它们的通信方法有三种,网络结构由Channel网管理,分别负责写和读。而对于Service & Client
,它们都拥有Transmitter & Receiver
,Service
接收request发送response,Client
接收response发送request,通信方式为RTPS,网络结构由Service网管理。
所以Service & Client
模式底层的通信还是借助于Transport
,只不过是做成了service-client方式,上层也不像Writer, Reader
那样借助DataVisitor
等结构来通知,而是直接调用回调函数即可。它们也是依附于Node
存在的,由Node
的成员NodeServiceImpl
提供创建方法。
ServiceManager
和其他Manager类似,也是保存了servers_, clients_
两个列表,当有新的加入时,更新两张表,同时提供查询功能。它最主要的一个应用在cyber/service/client_base.h
的WaitForServiceNanoseconds()
函数中调用了ServiceManager::HasService()
函数来确定某个service是否存在。
最后仍旧盗一张总的结构图
参考链接
百度Apollo系统学习-Cyber RT 通信-上层
百度Apollo系统学习-Cyber RT 通信-底层
Apollo 3.5 Cyber - Service Discovery 模塊