https://blog.csdn.net/weixin_44450715/article/details/86314193
上一篇說完了shared memory篇後,也要把餘下的Intra 和 rtps 篇給補完了
先說重點,Intra模式不是做多進程通訊的,而是做單進程內部通訊的。
而其架構基本就是把shared memory版本中的所有shared memory操作,線程安全的代碼去掉就是了。
所以一定要先看上一篇
簡單看一下其IntraDispatcher
和IntraTransmitter
就會明白了(IntraReceiver
的實現和ShmReceiver
是一樣的)
// IntraDispatcher
template <typename MessageT>
void IntraDispatcher::OnMessage(uint64_t channel_id,
const std::shared_ptr<MessageT>& message,
const MessageInfo& message_info) {
if (is_shutdown_.load()) {
return;
}
ADEBUG << "intra on message, channel:"
<< common::GlobalData::GetChannelById(channel_id);
ListenerHandlerBasePtr* handler_base = nullptr;
if (msg_listeners_.Get(channel_id, &handler_base)) {
if ((*handler_base)->IsRawMessage()) {
auto handler =
std::dynamic_pointer_cast<ListenerHandler<message::RawMessage>>(
*handler_base);
auto msg = std::make_shared<message::RawMessage>();
message::SerializeToString(*message, &msg->message);
handler->Run(msg, message_info);
} else {
auto handler =
std::dynamic_pointer_cast<ListenerHandler<MessageT>>(*handler_base);
if (handler == nullptr) {
AERROR << "please ensure that readers with the same channel["
<< common::GlobalData::GetChannelById(channel_id)
<< "] in the same process have the same message type";
return;
}
handler->Run(message, message_info);
}
}
}
// IntraTransmitter
template <typename M>
bool IntraTransmitter<M>::Transmit(const MessagePtr& msg,
const MessageInfo& msg_info) {
if (!this->enabled_) {
ADEBUG << "not enable.";
return false;
}
dispatcher_->OnMessage(channel_id_, msg, msg_info);
return true;
}
無錯,就是在Transmit
時直接把所有相關callback調用,直接簡單。
要了解RTPS模式,要先了解rtps(real-time publish subscribe)是甚麼
以下有一些有用的資料
https://wiki.wireshark.org/Protocols/rtps
rtps基本就是一個用來做實時publish subscribe model
通訊的protocol
Apollo用了eprosima-fast-rtps的rtps實現。
可以先看一下Docs
At the top of RTPS, we find the Domain, which defines a separate plane of communication.
Several domains can coexist at the same time independently.
A domain contains any number of Participants, elements capable of sending and receiving data.
To do this, the participants use their Endpoints:
Reader: Endpoint able to receive data.
Writer: Endpoint able to send data.
A Participant can have any number of writer and reader endpoints.
比較不一樣的是
Communication revolves around Topics, which define the data being exchanged.
Topics don’t belong to any participant in particular; instead,
all interested participants keep track of changes to the topic data and make sure to keep each other up to date.
The unit of communication is called a Change, which represents an update to a topic.
Endpoints register these changes on their History, a data structure that serves as a cache for recent changes.
When you publish a change through a writer endpoint, the following steps happen behind the scenes:
The change is added to the writer’s history cache.
The writer informs any readers it knows about.
Any interested (subscribed) readers request the change.
After receiving data, readers update their history cache with the new change.
By choosing Quality of Service policies, you can affect how these history caches are managed in several ways, but the communication loop remains the same. You can read more information in Configuration.
比起shared memory版的, rtps版多了
不過這幾點對用家影響不大,可直接無視
eprosima-fast-rtps的rtps實現邏輯如上
其concept和apollo內部的channel,receiver,transmitter是差不多的.可是要小心
Domain
的Participant
,可以互相通訊。而cyber中,所有的channel都在用同一個Domain
,而設定channel的方法,是設定PublisherAttributes
和SubscriberAttributes
中的一個field: topic
把RTPS的class也加到了進去圖中了。來說一下rtps模式中獨有的class吧
UnderlayMessage
,UnderlayMessageType
:UnderlayMessage是在rtps通訊中用到的消息類型,不管是甚麼消息類型都會先轉為char buffer(string),然後放到UnderlayMessage的data
變量中,再做傳送。而UnderlayMessageType是用來做register的。這兩個class的大部份代碼是由fast rtcp中一個idl工具生成的,不用深究AttributesFiller
,QosProfileConf
: 要用人家fastrtps
的庫,就要跟人家的規距,做一堆配置。配置多了怎辦,寫一個wrapper讓其直接按全局的config去自動配置SubListener
: Subscriber + Listener. 負責處理eprosima::fastrtps::Subscriber
在收到新message時,調用Listener(Listener == callback)RtpsReceiver
,RtpsDispatcher
,RtpsTransmitter
,就是 Receiver
,Dispatcher
,Transmitter
的具體實現。至於負責甚麼,可以看上一篇而rtps模式下的很多細節都藏在fastrtps
這庫中了,在cyber中只要把createSubscriber
跟createPublisher
設定好,就一切完事了
// Study: Create a subscriber, and call the callback when message come
new_sub.sub_listener = std::make_shared<SubListener>(
std::bind(&RtpsDispatcher::OnMessage, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3));
new_sub.sub = eprosima::fastrtps::Domain::createSubscriber(
participant_->fastrtps_participant(), sub_attr,
new_sub.sub_listener.get());
// Study: Create a publisher, and publish the data to subscriber
eprosima::fastrtps::PublisherAttributes pub_attr;
RETURN_IF(!AttributesFiller::FillInPubAttr(
this->attr_.channel_name(), this->attr_.qos_profile(), &pub_attr));
publisher_ = eprosima::fastrtps::Domain::createPublisher(
participant_->fastrtps_participant(), pub_attr);
```