第一篇 一个例子
车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用
第二篇fastdds的组成
车载消息中间件FastDDS 源码解析(二)RtpsParticipant的创建(上)
车载消息中间件FastDDS 源码解析(三)RtpsParticipant的创建(中)
车载消息中间件FastDDS 源码解析(四)RtpsParticipant的创建(下)
车载消息中间件FastDDS 源码解析(五)BuiltinProtocols(上)
车载消息中间件FastDDS 源码解析(六)BuiltinProtocols(中)EDP
车载消息中间件FastDDS 源码解析(七)BuiltinProtocols(下)WLP&TypeLookupManager
车载消息中间件FastDDS 源码解析(八)TimedEvent
第三篇组网建立连接
pdp建连
车载消息中间件FastDDS 源码解析(十)发送第一条PDP消息(上)
FastDDS 源码解析(十二)发送第一条PDP消息(下)---异步发送
FastDDS 源码解析(十三)发送第一条PDP消息---跨进程发送
FastDDS 源码解析(十六)处理PDP消息——PDP匹配
EDP建连
FastDDS 源码解析(十七)处理PDP消息——EDP匹配
FastDDS 源码解析(十八)EDP阶段发送心跳heartbeat
FastDDS 源码解析(十九)EDP阶段处理heartbeat消息,发送acknack消息
1.StatefulWriter处理acknack消息
1.1时序图
sequenceDiagram
participant UDPChannelResource
participant ReceiverResource
participant MessageReceiver
participant StatefulWriter
participant ReaderProxy
UDPChannelResource ->> UDPChannelResource: 1.perform_listen_operation()
UDPChannelResource ->> UDPChannelResource: 2.Receive()
UDPChannelResource ->> ReceiverResource: 3.OnDataReceived()
ReceiverResource ->> MessageReceiver: 4.processCDRMsg()
MessageReceiver ->> MessageReceiver: 5.proc_Submsg_Acknack()
MessageReceiver ->> StatefulWriter: 6.process_acknack()
StatefulWriter ->> ReaderProxy: 7.check_and_set_acknack_count()
StatefulWriter ->> ReaderProxy: 8.acked_changes_set()
StatefulWriter ->> ReaderProxy: 9.requested_changes_set()
StatefulWriter ->> StatefulWriter: 10.nack_response_event_.restart_timer()
StatefulWriter ->> ReaderProxy: 11.process_initial_acknack()
StatefulWriter ->> StatefulWriter: 12.check_acked_status()
1.-4.可以参看FastDDS 源码解析(十九)EDP阶段处理heartbeat消息,发送acknack消息1.1节内容
5.proc_Submsg_Acknack 解析各个字节的消息,解析完成后主要是调用StatefulWriter的process_acknack
6.StatefulWriter的process_acknack 主要调用了如下函数
a.ReaderProxy::check_and_set_acknack_count 见7
b.ReaderProxy::acked_changes_set 见8
c.ReaderProxy::requested_changes_set 见9
d.ReaderProxy::process_initial_acknack 见10
e.check_acked_status 见11
7.ReaderProxy::check_and_set_acknack_count
主要是查看一下这个acknack消息是不是最新的acknack,不是的话不处理,如果是的话处理这个acknack消息
8:ReaderProxy::acked_changes_set
传入的参数是readerSNState的base,
根据readerSNState的base,增加或者减少changes_for_reader_ 中的消息。
9:ReaderProxy::requested_changes_set
将changes_for_reader_ 中相应的SequenceNumber的消息状态设置成REQUESTED,nack_response_event_ 启动。
如果没找到这个SequenceNumber的消息,相当于这个消息在writer这儿没有,将这个SequenceNumber放入gap_builder,为之后构建gap消息做准备。
10.nack_response_event_ 启动,这个启动就是将请求的数据发出去。
11:ReaderProxy::process_initial_acknack
如果是本地的reader发送过来的acknack,则将所有状态为 UNACKNOWLEDGED的消息状态设置为UNSENT,然后将消息通过flow_controller_发送出去
如果是remote的reader,writer发送heartbeat给reader
12:check_acked_status
一些新的被所有的reader收到的消息,需要处理一下调用onWriterChangeReceivedByAll,如果消息是VOLATILE类型的,会被从history 中删除。
1.2源码
步骤5:
bool MessageReceiver::proc_Submsg_Acknack(
CDRMessage_t* msg,
SubmessageHeader_t* smh) const
{
eprosima::shared_lock<eprosima::shared_mutex> guard(mtx_);
bool endiannessFlag = (smh->flags & BIT(0)) != 0;
bool finalFlag = (smh->flags & BIT(1)) != 0;
//Assign message endianness
if (endiannessFlag)
{
msg->msg_endian = LITTLEEND;
}
else
{
msg->msg_endian = BIGEND;
}
GUID_t readerGUID;
GUID_t writerGUID;
readerGUID.guidPrefix = source_guid_prefix_;
CDRMessage::readEntityId(msg, &readerGUID.entityId);
writerGUID.guidPrefix = dest_guid_prefix_;
CDRMessage::readEntityId(msg, &writerGUID.entityId);
SequenceNumberSet_t SNSet = CDRMessage::readSequenceNumberSet(msg);
uint32_t Ackcount;
if (!CDRMessage::readUInt32(msg, &Ackcount))
{
EPROSIMA_LOG_WARNING(RTPS_MSG_IN, IDSTRING "Unable to read ackcount from message");
return false;
}
//Look for the correct writer to use the acknack
for (RTPSWriter* it : associated_writers_)
{
bool result;
if (it->process_acknack(writerGUID, readerGUID, Ackcount, SNSet, finalFlag, result))
{
if (!result)
{
EPROSIMA_LOG_INFO(RTPS_MSG_IN, IDSTRING "Acknack msg to NOT stateful writer ");
}
return result;
}
}
EPROSIMA_LOG_INFO(RTPS_MSG_IN, IDSTRING "Acknack msg to UNKNOWN writer (I looked through "
<< associated_writers_.size() << " writers in this ListenResource)");
return false;
}
这儿读取了几个比较重要的参数
Ackcount acknack的序列号,
SNSet 参考FastDDS 源码解析(十九)EDP阶段处理heartbeat消息,发送acknack消息抓包的部分,可以知道这是表示,有哪些消息已经收到,有哪些还没收到 步骤6:
bool StatefulWriter::process_acknack(
const GUID_t& writer_guid,
const GUID_t& reader_guid,
uint32_t ack_count,
const SequenceNumberSet_t& sn_set,
bool final_flag,
bool& result)
{
std::unique_lock<RecursiveTimedMutex> lock(mp_mutex);
result = (m_guid == writer_guid);
if (result)
{
SequenceNumber_t received_sequence_number = sn_set.empty() ? sn_set.base() : sn_set.max();
if (received_sequence_number <= next_sequence_number())
{
for_matched_readers(matched_local_readers_, matched_datasharing_readers_, matched_remote_readers_,
[&](ReaderProxy* remote_reader)
{
if (remote_reader->guid() == reader_guid)
{
if (remote_reader->check_and_set_acknack_count(ack_count))
{
// Sequence numbers before Base are set as Acknowledged.
remote_reader->acked_changes_set(sn_set.base());
if (sn_set.base() > SequenceNumber_t(0, 0))
{
// Prepare GAP for requested samples that are not in history or are irrelevants.
RTPSMessageGroup group(mp_RTPSParticipant, this, remote_reader->message_sender());
RTPSGapBuilder gap_builder(group);
if (remote_reader->requested_changes_set(sn_set, gap_builder, get_seq_num_min()))
{
nack_response_event_->restart_timer();
}
else if (!final_flag)
{
periodic_hb_event_->restart_timer();
}
gap_builder.flush();
}
else if (sn_set.empty() && !final_flag)
{
// This is the preemptive acknack.
// 是否是初始化的acknack
if (remote_reader->process_initial_acknack([&](ChangeForReader_t& change_reader)
{
assert(nullptr != change_reader.getChange());
flow_controller_->add_old_sample(this, change_reader.getChange());
}))
{
if (remote_reader->is_remote_and_reliable())
{
// Send heartbeat if requested
send_heartbeat_to_nts(*remote_reader, false, true);
periodic_hb_event_->restart_timer();
}
}
if (remote_reader->is_local_reader() && !remote_reader->is_datasharing_reader())
{
intraprocess_heartbeat(remote_reader);
}
}
// Check if all CacheChange are acknowledge, because a user could be waiting
// for this, or some CacheChanges could be removed if we are VOLATILE
check_acked_status();
}
return true;
}
return false;
}
);
}
else
{
print_inconsistent_acknack(writer_guid, reader_guid, sn_set.base(), received_sequence_number,
next_sequence_number());
}
}
return result;
}
1.ReaderProxy::check_and_set_acknack_count
acknack_count 是这个acknack message的序列号,如果这个是最新的序列号,那么将ReaderProxy中last_acknack_count设置成最新,然后处理这个acknack,如果不是最新的的序列号,直接将这个acknack忽略掉。
2.ReaderProxy::acked_changes_set
传入的参数是readerSNState的base,
acknack发送了一些消息的接受状态,readerSNState的base是这些消息中最小的SequenceNumber,SequenceNumber < base的消息状态,不会再从reader同步过来了。
根据readerSNState的base,增加或者减少changes_for_reader_ 中的消息。
3.ReaderProxy::requested_changes_set
将changes_for_reader_ 中相应的SequenceNumber的消息状态设置成REQUESTED,nack_response_event_ 重新启动
如果没找到这个SequenceNumber的消息,相当于这个消息在writer这儿没有,将这个SequenceNumber放入gap_builder,为之后构建gap消息做准备。
4.process_initial_acknack
什么是initial_acknack,可以参考17篇FastDDS 源码解析(十七)处理PDP消息——EDP匹配,就是在没有收到heartbeat的情况下发送的acknack消息
如果是本地的reader发送过来的acknack,则将所有状态为 UNACKNOWLEDGED的消息状态设置为UNSENT,然后将消息通过flow_controller_发送出去
如果是remote的reader,writer发送heartbeat给reader
5.check_acked_status
根据每个reader的changes_low_mark_,找到最小值min_low_mark,表示小于min_low_mark的消息已经被所有的reader收到了。
一些新的被所有的reader收到的消息,需要处理一下调用onWriterChangeReceivedByAll,如果消息是VOLATILE类型的,会被从history 中删除。
步骤7:
bool check_and_set_acknack_count(
uint32_t acknack_count)
{
if (last_acknack_count_ < acknack_count)
{
last_acknack_count_ = acknack_count;
return true;
}
return false;
}
acknack_count 是这个acknack message的序列号,
last_acknack_count_ 记录的是最新的acknack 的序列号,如果收到的acknack的序列号大于last_acknack_count_ ,则将acknack_count 赋值给last_acknack_count_,返回true。否则返回false 。
步骤8:
void ReaderProxy::acked_changes_set(
const SequenceNumber_t& seq_num)
{
SequenceNumber_t future_low_mark = seq_num;
if (seq_num > changes_low_mark_)
{
ChangeIterator chit = find_change(seq_num, false);
// continue advancing until next change is not acknowledged
while (chit != changes_for_reader_.end()
&& chit->getSequenceNumber() == future_low_mark
&& chit->getStatus() == ACKNOWLEDGED)
{
++chit;
++future_low_mark;
}
changes_for_reader_.erase(changes_for_reader_.begin(), chit);
}
else
{
future_low_mark = changes_low_mark_ + 1;
if (seq_num == SequenceNumber_t() && durability_kind_ != DurabilityKind_t::VOLATILE)
{
// Special case. Currently only used on Builtin StatefulWriters
// after losing lease duration, and on late joiners to set
// changes_low_mark_ to match that of the writer.
SequenceNumber_t min_sequence = writer_->get_seq_num_min();
if (min_sequence != SequenceNumber_t::unknown())
{
SequenceNumber_t current_sequence = seq_num;
if (seq_num < min_sequence)
{
current_sequence = min_sequence;
}
future_low_mark = current_sequence;
bool should_sort = false;
for (; current_sequence <= changes_low_mark_; ++current_sequence)
{
// Skip all consecutive changes already in the collection
ChangeConstIterator it = find_change(current_sequence);
while ( it != changes_for_reader_.end() &&
current_sequence <= changes_low_mark_ &&
it->getSequenceNumber() == current_sequence)
{
++current_sequence;
++it;
}
if (current_sequence <= changes_low_mark_)
{
CacheChange_t* change = nullptr;
if (writer_->mp_history->get_change(current_sequence, writer_->getGuid(), &change))
{
should_sort = true;
ChangeForReader_t cr(change);
cr.setStatus(UNACKNOWLEDGED);
changes_for_reader_.push_back(cr);
}
}
}
// Keep changes sorted by sequence number
if (should_sort)
{
std::sort(changes_for_reader_.begin(), changes_for_reader_.end(), ChangeForReaderCmp());
}
}
else if (!is_local_reader())
{
future_low_mark = writer_->next_sequence_number();
}
}
}
changes_low_mark_ = future_low_mark - 1;
}
传入的参数是readerSNState的base。
acknack发送了一些消息的接受状态,readerSNState的base是这些消息中最小的SequenceNumber,SequenceNumber < base的消息状态,不会再从reader同步过来了。
ResourceLimitedVector<ChangeForReader_t, std::true_type> changes_for_reader_ 这个属性记录了消息的状态。
ReaderProxy中有一个属性changes_low_mark_ ,表示的是在这个 changes_low_mark_之前的消息,不再记录状态。
所以readerSNState的base表示的是reader 会同步消息的最小的SequenceNumber,changes_low_mark_ 表示的是本地记录状态的消息最小的SequenceNumber。
如果readerSNState的base > changes_low_mark_ , 则对于本地记录消息状态的消息,SequenceNumber从低到高遍历,如果消息是acked状态,则将消息从消息队列中移除,然后修改 changes_low_mark_
如果readerSNState的base <= changes_low_mark ,这个主要是用在builtin的statefulWriter,statefulReader上,如果有个新加入的Reader,同时durability_kind >VOLATILE(就是需要发送加入之前的所有消息),那么需要将statefulWriter中所有的消息的状态设置成UNACKNOWLEDGED,加入到 changes_for_reader_ 中去。
步骤11:
bool ReaderProxy::requested_changes_set(
const SequenceNumberSet_t& seq_num_set,
RTPSGapBuilder& gap_builder,
const SequenceNumber_t& min_seq_in_history)
{
bool isSomeoneWasSetRequested = false;
if (SequenceNumber_t::unknown() != min_seq_in_history)
{
seq_num_set.for_each([&](SequenceNumber_t sit)
{
ChangeIterator chit = find_change(sit, true);
if (chit != changes_for_reader_.end())
{
if (UNACKNOWLEDGED == chit->getStatus())
{
chit->setStatus(REQUESTED);
chit->markAllFragmentsAsUnsent();
isSomeoneWasSetRequested = true;
}
}
else if ((sit >= min_seq_in_history) && (sit > changes_low_mark_))
{
gap_builder.add(sit);
}
});
}
if (isSomeoneWasSetRequested)
{
EPROSIMA_LOG_INFO(RTPS_READER_PROXY, "Requested Changes: " << seq_num_set);
}
return isSomeoneWasSetRequested;
}
传入的参数是sn_set,就是acknack中 请求的消息的 SequenceNumber
将changes_for_reader_ 中相应的SequenceNumber的消息状态设置成REQUESTED
如果没找到这个SequenceNumber的消息,相当于这个消息在writer这儿没有,将这个SequenceNumber放入gap_builder
我们知道gap消息是writer发送给reader的消息,标识HistoryCache中的一些Change不再可用,也不会再发给Reader。
这个gap_builder就是构建这个gap消息的。
步骤12:
void StatefulWriter::check_acked_status()
{
std::unique_lock<RecursiveTimedMutex> lock(mp_mutex);
bool all_acked = true;
bool has_min_low_mark = false;
// #8945 If no readers matched, notify all old changes.
SequenceNumber_t min_low_mark = mp_history->next_sequence_number() - 1;
//根据每个reader的changes_low_mark_,找到最小值min_low_mark,表示小于min_low_mark的消息已经被所有的reader收到了
for_matched_readers(matched_local_readers_, matched_datasharing_readers_, matched_remote_readers_,
[&all_acked, &has_min_low_mark, &min_low_mark](ReaderProxy* reader)
{
//changes_low_mark_ 对一个readerproxy来说,seqnumber <changes_low_mark_的消息都已经acknowledged了
SequenceNumber_t reader_low_mark = reader->changes_low_mark();
if (reader_low_mark < min_low_mark || !has_min_low_mark)
{
has_min_low_mark = true;
min_low_mark = reader_low_mark;
}
if (reader->has_changes())
{
all_acked = false;
}
return false;
}
);
bool something_changed = all_acked;
SequenceNumber_t min_seq = get_seq_num_min();
if (min_seq != SequenceNumber_t::unknown())
{
// In the case where we haven't received an acknack from a recently matched reader,
// min_low_mark will be zero, and no change will be notified as received by all
if (next_all_acked_notify_sequence_ <= min_low_mark)
{
if ((mp_listener != nullptr) && (min_low_mark >= get_seq_num_min()))
{
// We will inform backwards about the changes received by all readers, starting
// on min_low_mark down until next_all_acked_notify_sequence_. This way we can
// safely proceed with the traversal, in case a change is removed from the history
// inside the callback
History::iterator history_end = mp_history->changesEnd();
History::iterator cit =
std::lower_bound(mp_history->changesBegin(), history_end, min_low_mark,
[](
const CacheChange_t* change,
const SequenceNumber_t& seq)
{
return change->sequenceNumber < seq;
});
if (cit != history_end && (*cit)->sequenceNumber == min_low_mark)
{
++cit;
}
SequenceNumber_t seq{};
SequenceNumber_t end_seq = min_seq > next_all_acked_notify_sequence_ ?
min_seq : next_all_acked_notify_sequence_;
// The iterator starts pointing to the change inmediately after min_low_mark
--cit;
do
{
// Avoid notifying changes before next_all_acked_notify_sequence_
CacheChange_t* change = *cit;
seq = change->sequenceNumber;
if (seq < next_all_acked_notify_sequence_)
{
break;
}
// Change iterator before it possibly becomes invalidated
if (cit != mp_history->changesBegin())
{
--cit;
}
// Notify reception of change (may remove that change on VOLATILE writers)
mp_listener->onWriterChangeReceivedByAll(this, change);
// Stop if we got to either next_all_acked_notify_sequence_ or the first change
} while (seq > end_seq);
}
next_all_acked_notify_sequence_ = min_low_mark + 1;
}
if (min_low_mark >= get_seq_num_min())
{
may_remove_change_ = 1;
}
min_readers_low_mark_ = min_low_mark;
something_changed = true;
}
if (all_acked)
{
std::unique_lock<std::mutex> all_acked_lock(all_acked_mutex_);
SequenceNumber_t next_seq = mp_history->next_sequence_number();
next_all_acked_notify_sequence_ = next_seq;
min_readers_low_mark_ = next_seq - 1;
all_acked_ = true;
all_acked_cond_.notify_all();
}
if (something_changed)
{
may_remove_change_cond_.notify_one();
}
}
根据每个reader的changes_low_mark_,找到最小值min_low_mark,表示小于min_low_mark的消息已经被所有的reader收到了。
一些新的被所有的reader收到的消息,需要处理一下调用onWriterChangeReceivedByAll,如果消息是VOLATILE类型的,会被从history 中删除。
步骤10 这边详细介绍一下
nack_response_event_ = new TimedEvent(
pimpl->getEventResource(),
[&]() -> bool
{
perform_nack_response();
return false;
},
TimeConv::Time_t2MilliSecondsDouble(m_times.nackResponseDelay));
这个可以参考 第6篇 车载消息中间件FastDDS 源码解析(六)BuiltinProtocols(中)EDP,这个TimedEvent是在StatefulWriter初始化的时候初始化的。当收到acknack消息之后就会启动这个nack_response_event_,响应时间默认是5ms
void StatefulWriter::perform_nack_response()
{
std::unique_lock<RecursiveTimedMutex> lock(mp_mutex);
uint32_t changes_to_resend = 0;
for (ReaderProxy* reader : matched_remote_readers_)
{
changes_to_resend += reader->perform_acknack_response([&](ChangeForReader_t& change)
{
// This labmda is called if the ChangeForReader_t pass from REQUESTED to UNSENT.
assert(nullptr != change.getChange());
flow_controller_->add_old_sample(this, change.getChange());
}
);
}
lock.unlock();
// Notify the statistics module
on_resent_data(changes_to_resend);
}
uint32_t ReaderProxy::perform_acknack_response(
const std::function<void(ChangeForReader_t& change)>& func)
{
return convert_status_on_all_changes(REQUESTED, UNSENT, func);
}
uint32_t ReaderProxy::convert_status_on_all_changes(
ChangeForReaderStatus_t previous,
ChangeForReaderStatus_t next,
const std::function<void(ChangeForReader_t& change)>& func)
{
assert(previous > next);
// NOTE: This is only called for REQUESTED=>UNSENT (acknack response) or
// UNDERWAY=>UNACKNOWLEDGED (nack supression)
uint32_t changed = 0;
for (ChangeForReader_t& change : changes_for_reader_)
{
if (change.getStatus() == previous)
{
++changed;
change.setStatus(next);
if (func)
{
func(change);
}
}
}
return changed;
}
这两个函数结合起来一起看,扫描所有的远端的reader,
将reader中message状态为REQUESTED的消息调用flow_controller_->add_old_sample,准备重新发送。同时将message状态为REQUESTED的消息的状态设置为UNSENT。
2.ChangeForReader_t的状态
classDiagram
StatefulWriter *-- ReaderProxy
StatefulWriter *-- periodic_hb_event_
StatefulWriter *-- nack_response_event_
ReaderProxy *-- intitial_heartbeat_event_
ReaderProxy *-- nack_supression_event_
class StatefulWriter{
+ResourceLimitedVector<ReaderProxy*> matched_local_readers_
+ResourceLimitedVector<ReaderProxy*> matched_datasharing_readers_
+ResourceLimitedVector<ReaderProxy*> matched_remote_readers_
TimedEvent* periodic_hb_event_
TimedEvent* nack_response_event_
}
class ReaderProxy{
+TimedEvent* intitial_heartbeat_event_
+TimedEvent* nack_supression_event_
+ResourceLimitedVector<ChangeForReader_t, std::true_type> changes_for_reader_
}
StatefulWriter有3个队列存放了和这个StatefulWriter对应的reader的信息,也就是这个reader的代理,ReaderProxy。
ReaderProxy中保存了一个队列,保存了各个消息的状态。然后根据这些消息的状态选择重发。
enum ChangeForReaderStatus_t
{
UNSENT = 0, //!< UNSENT
REQUESTED = 1, //!< REQUESTED
UNACKNOWLEDGED = 2, //!< UNACKNOWLEDGED
ACKNOWLEDGED = 3, //!< ACKNOWLEDGED
UNDERWAY = 4 //!< UNDERWAY
};
在Writer这儿,一个消息的状态一共有5种。
UNSENT 就是还没发送,这是消息的初始状态
REQUESTED 是请求状态, 当收到acknack消息,被reader请求的消息,会被设置成REQUESTED
UNACKNOWLEDGED 是没有被reader收到
ACKNOWLEDGED 已经发送给reader,同时reader已经收到
UNDERWAY 已经发送给reader,但是还没有收到reader的acknack消息
第一篇 一个例子
车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用
第二篇fastdds的组成
车载消息中间件FastDDS 源码解析(二)RtpsParticipant的创建(上)
车载消息中间件FastDDS 源码解析(三)RtpsParticipant的创建(中)
车载消息中间件FastDDS 源码解析(四)RtpsParticipant的创建(下)
车载消息中间件FastDDS 源码解析(五)BuiltinProtocols(上)
车载消息中间件FastDDS 源码解析(六)BuiltinProtocols(中)EDP
车载消息中间件FastDDS 源码解析(七)BuiltinProtocols(下)WLP&TypeLookupManager