【FastRTPS】高級配置

來自:https://eprosima-fast-rtps.readthedocs.io/en/latest/advanced.html

參考《FastRTPS User Manual.odt》第六章第6.5節 版本1.5

 

網絡配置

FastRTPS支持多種傳輸層接口(插件架構),也可以開發符合FastRTPS的第三方傳輸層,因此高級用戶可以根據項目需要來自己設計。當前版本實現了UDPv4(默認)、UDPv6、TCPv4(1.5版本中沒有)、TCPv6(1.5版本中沒有),並在未來版本規劃了共享內存等傳輸方式。

當用戶沒有定義 Input / Output通道時,FastRTPS會內置網絡配置,使用IPv4協議,通過所有可用接口監聽發送,發送端口爲10040。

rtps.userTransports 自定義傳輸(比如發送、接收緩衝區大小)。

rtps.useBuiltinTransports false禁用內置傳輸

自定義UDPv4傳輸

//Create a udpv4 descriptor for the new transport.
auto custom_transport = std::make_shared<UDPv4TransportDescriptor>();
custom_transport->sendBufferSize = 9216;
custom_transport->receiveBufferSize = 9216;

//Disable the built-in Transport Layer.
participant_attr.rtps.useBuiltinTransports = false;

//Link the Transport Layer to the Participant.
participant_attr.rtps.userTransports.push_back(custom_transport);

自定義TCPv4傳輸(1.5版本不支持)

//Create a descriptor for the new transport.
auto tcp_transport = std::make_shared<TCPv4TransportDescriptor>();
tcp_transport->add_listener_port(5100);                           

//Set initial peers. remote listening port
Locator_t initial_peer_locator;
initial_peer_locator.kind = LOCATOR_KIND_TCPv4;
IPLocator::setIPv4(initial_peer_locator, "192.168.1.55");
initial_peer_locator.port = 5100;
participant_attr.rtps.builtin.initialPeersList.push_back(initial_peer_locator);

//Disable the built-in Transport Layer.
participant_attr.rtps.useBuiltinTransports = false;

//Link the Transport Layer to the Participant.
participant_attr.rtps.userTransports.push_back(tcp_transport);           

本地偵聽端口5100,連接到遠程地址192.168.1.55:5100

IPLocator(1.5版本不支持, 略)

Locator_t locator;
// Get & Set Physical Port
uint16_t physical_port = IPLocator::getPhysicalPort(locator);
IPLocator::setPhysicalPort(locator, 5555);

// Get & Set Logical Port
uint16_t logical_port = IPLocator::getLogicalPort(locator);
IPLocator::setLogicalPort(locator, 7400);

// Set WAN Address
IPLocator::setWan(locator, "80.88.75.55");

通道適應性

創建Participant的時候,管理 Input / Output通道的系資源會被保留下來。由於各種原因,可能無法打開管理指定通道的系統資源。所以FastRTPS使用了通道適應機制來確保在創建Participant的時候有一個最小的通道可用集,當指定的通道不可用時就返回特徵一致的通道。創建了新通道時,ParticipantAttribute也會被更新。也就是說,FastRTPS會始終維護有效的內置通道。

用戶定義的Writer / Reader,並沒有適應機制,也就意味着配置不正確時會導致申請資源失敗。也就是說,用戶需要確保設置的通道是有效的。

偵聽定位器:Locator_t

元組播定位器(metatraffic multicast):用於接收元組播數據,被用於內置端點,比如發現內置端點。

元單播定位器(metatraffic unicast):用於接收元單播數據,被用於內置端點,比如發現內置端點。

用戶組播定位器(user multicast):用於接收用戶組播數據,被用於用戶端點。

用戶單播定位器(user unicast):用於接收用戶單播數據,被用於用戶端點。

rtps.builtin.metatrafficMulticastLocatorList 自定義元組播定位器

rtps.builtin.metatrafficUnicastLocatorList 自定義元單播定位器

rtps.defaultMulticastLocatorList 自定義用戶組播定位器

rtps.defaultUnicastLocatorList 自定義用戶單播定位器

示例1:

eprosima::fastrtps::ParticipantAttributes part_attr;

// This locator will open a socket to listen network messages on UDPv4 port 22222 over multicast address 239.255.0.1
eprosima::fastrtps::rtps::Locator_t locator.set_IP4_address(239, 255, 0 , 1);
locator.port = 22222;

part_attr.rtps.builtin.metatrafficMulticastLocatorList.push_back(locator);

 接收239.255.0.1地址的多播消息,偵聽端口22222;

示例2:

eprosima::fastrtps::ParticipantAttributes part_attr;

// This locator will open a socket to listen network messages on UDPv4 port 22223 over network interface 192.168.0.1
eprosima::fastrtps::rtps::Locator_t locator.set_IP4_address(192, 168, 0 , 1);
locator.port = 22223;

part_attr.rtps.builtin.metatrafficUniicastLocatorList.push_back(locator);

接收發送到本機192.168.0.1上的單播消息,偵聽端口22223; 

locator.IP4_address=null時,FastRTPS會主動獲取本地網絡地址。

locator.port=0時,FastRTPS會給UDPv4通信分配一個通用端口 ,分配計算規則:

Traffic type Well-known port expression
Metatraffic multicast PB + DG * domainId + offsetd0
Metatraffic unicast PB + DG * domainId + offsetd1 + PG * participantId
User multicast PB + DG * domainId + offsetd2
User unicast PB + DG * domainId + offsetd3 + PG * participantId

DG--域ID增量(domainid gain),屬性:rtps.port.domainIDGain,默認值:250

PG--參與者ID增量(participantID gain),屬性:rtps.port.participantIDGain,默認值:2

PB--端口基數(port base number),屬性:rtps.port.portBase,默認值:7400

offset--偏移,屬性rtps.port.offsetd,默認值:offsetd0 = 0,offsetd1 = 10, offsetd2 = 1, offsetd3 = 11

發送定位器

用於創建發送消息的網絡端點。(例中,通過192.168.0.1:34000向網絡發送消息)

eprosima::fastrtps::ParticipantAttributes part_attr;

// This locator will create a socket to send network message on UDPv4 port 34000 over network interface 192.168.0.1
Locator_t locator.set_IP4_address(192.168.0.1);
locator.port = 34000;

part_attr.rtps.defaultOutLocatorList.push_back(locator);

locator.IP4_address=null時,FastRTPS會主動獲取本地網絡地址。

locator.port=0時,FastRTPS會給UDPv4通信分配一個通用端口 ,分配計算規則:

初始網絡發現定位器(Initial peers)

用於創建網絡發現的網絡端點。(例中,通過192.168.0.2:7600發送網絡發現消息,這個消息是週期性發送的)

默認情況下,FastRTPS也將這些定位器用作元組播定位器(Metatraffic Multicast Locators)

eprosima::fastrtps::ParticipantAttributes part_attr;

// This locator configures as initial peer the UDPv4 address 192.168.0.2:7600.
// Initial discovery network messages will send to this UDPv4 address.
Locator_t locator.set_IP4_address(192.168.0.2);
locator.port = 7600;

part_attr.rtps.builtin.initialPeersList.push_back(locator);

白名單

有時候用戶需要禁用某些網絡接口,防止通過這些接口進行連接或者發送數據。這時候可以設置 interfaceWhiteList屬性來設置要用的網絡接口。

UDPv4TransportDescriptor descriptor;
descriptor.interfaceWhiteList.emplace_back("127.0.0.1");

禁用多播

只設置單播定位器和初始網絡發現定位器。

eprosima::fastrtps::ParticipantAttributes part_attr;

// Metatraffic Multicast Locator List will be empty.
// Metatraffic Unicast Locator List will contain one locator, with null address and null port.
// Then eProsima Fast RTPS will use all network interfaces to receive network messages using a well-known port.
Locator_t default_unicast_locator;
participant_attr_.rtps.builtin.metatrafficUnicastLocatorList.push_back(default_unicast_locator);

// Initial peer will be UDPv4 addresss 192.168.0.1. The port will be a well-known port.
// Initial discovery network messages will be sent to this UDPv4 address.
Locator_t initial_peer;
initial_peer.set_IP4_address(192, 168, 0, 1);
participant_attr_.rtps.builtin.initialPeersList.push_back(initial_peer);

流控制

參考 Publisher-Subscriber層Writer-Reader層 中的流控制策略部分。

發送大數據

參考 Publisher-Subscriber層Writer-Reader層 中的數據包拆分部分。

如何提高發送大數據的性能?

假設,發送一個9.9MB大小的文件,帶寬是100MB/s,緩衝區大小65kb(也就是說文件數據得被分成1100多個包)。

1. 設置爲異步(pubAttr.qos.m_publishMode / writerAttr.mode

2. 可靠模式(pubAttr.qos.m_reliability.kind / writerAttr.endpoint.reliabilityKind):不允許數據分片丟失,如果是音頻或視頻文件,就可以用高效模式,因爲丟失一兩幀並不會有什麼影響。

3. 降低心跳週期(pubAttr.times.heartbeatPeriod / writerAttr.times.heartbeatPeriod):大量的數據分片會降低傳輸速度,降低心跳週期會增加網絡中消息的數量,但是同時,當數據分片丟失的時候,會加速系統響應。

4. 增加流控制(pubAttr.terminalThroughputController / writerAttr.throughputController):防止發送數據佔用所有的帶寬,比如限制在5MB/s

5. 增加UDP緩衝區大小:linux系統默認最大socket緩衝區,命令如下:

# temporary modify socket buffer size
sysctl -w net.ipv4.udp_mem="102400 873800 16777216"
sysctl -w net.core.netdev_max_backlog="30000"
sysctl -w net.core.rmem_max="16777216"
sysctl -w net.core.wmem_max="16777216"

# permanent modify socket buffer size
echo 'net.core.wmem_max=12582912' >> /etc/sysctl.conf
echo 'net.core.rmem_max=12582912' >> /etc/sysctl.conf

# get max socket buffer size
sudo sysctl -a | grep net.core.wmem_max
sudo sysctl -a | grep net.core.rmem_max

設置FastRTPS中的默認緩衝區大小:

participant_attr.rtps.sendSocketBufferSize = 1048576;
participant_attr.rtps.listenSocketBufferSize = 4194304;

 發現

自動發現分爲兩個階段:Participant發現(PDP)、EndPoint發現(EDP)

PDP(Participant Discovery Phase):週期性的發送自己的信息,先發現Participant,再匹配EndPoint。

注意:發送網絡配置:Initial peers

EDP(EndPoint Discovery Phase):向遠程Participant 發送實體信息,並處理遠程Participant的實體信息,並檢查哪些實體可以匹配。這個階段也可以用不發送任何消息的靜態版本(直接從XML加載)實現,靜態發現適合於網絡帶寬有限且Publisher和Subscriber已知的情況。

默認啓用發現,禁用發現代碼:

participant_attr.rtps.builtin.use_SIMPLE_RTPSParticipantDiscoveryProtocol = false;

 使用靜態發現:

ParticipantAttributes participant_attr;
participant_attr.rtps.builtin.use_SIMPLE_EndpointDiscoveryProtocol = false;
participant_attr.rtps.builtin.use_STATIC_EndpointDiscoveryProtocol = true;
participant_attr.rtps.builtin.setStaticEndpointXMLFilename("ParticipantWithASubscriber.xml");

ParticipantWithASubscriber.xml:例子中,會匹配遠程Participant中的一個Reader,這個Reader會接收本程序中的Writer發出的數據。

<staticdiscovery>
    <participant>
        <name>HelloWorldSubscriber</name>
        <reader>
            <userId>3</userId>
            <entityId>4</userId>
            <topicName>HelloWorldTopic</topicName>
            <topicDataType>HelloWorld</topicDataType>
        </reader>
    </participant>
</staticdiscovery>

靜態發現XML中的 reader / writer 參數:

參數 類型
userId numeric
entityId numeric
expectsInlineQos true / false (只有配置reader時有效)
topicName text
topicDataType text
topicKind NO_KEY / WITH_KEY
reliabilityQos BEST_EFFORT_RELIABILITY_QOS / RELIABLE_RELIABILITY_QOS
unicastLocator.address text
unicastLocator.port numeric
multicastLocator.address text
multicastLocator.port numeric
durabilityQos

VOLATILE_DURABILITY_QOS / TRANSIENT_LOCAL_DURABILITY_QOS /

TRANSIENT_DURABILITY_QOS

ownershipQos.kind SHARED_OWNERSHIP_QOS / EXCLUSIVE_OWNERSHIP_QOS
partitionQos text
livelinessQos.kind AUTOMATIC_LIVELINESS_QOS / MANUAL_BY_PARTICIPANT_LIVELINESS_QOS / MANUAL_BY_TOPIC_LIVELINESS_QOS
livelinessQos.leaseDuration_ms numeric

訂閱發現消息

Participant在發現的過程中會發送一些元數據。用戶可以定義ParticipantListener來處理髮現消息。內置協議處理完消息後就會調用ParticipantListener中的回調函數。

class CustomParticipantListener : public eprosima::fastrtps::ParticipantListener
{
    /* Custom Listener onSubscriberDiscovery */
    void onSubscriberDiscovery(
            eprosima::fastrtps::Participant * participant,
            eprosima::fastrtps::rtps::ReaderDiscoveryInfo && info) override
    {
        (void)participant;
        switch(info.status) {
            case eprosima::fastrtps::rtps::ReaderDiscoveryInfo::DISCOVERED_READER:
                /* Process the case when a new subscriber was found in the domain */
                cout << "New subscriber for topic '" << info.info.topicName() << "' of type '" << info.info.typeName() << "' discovered";
                break;
            case eprosima::fastrtps::rtps::ReaderDiscoveryInfo::CHANGED_QOS_READER:
                /* Process the case when a subscriber changed its QOS */
                break;
            case eprosima::fastrtps::rtps::ReaderDiscoveryInfo::REMOVED_READER:
                /* Process the case when a subscriber was removed from the domain */
                cout << "Subscriber for topic '" << info.info.topicName() << "' of type '" << info.info.typeName() << "' left the domain.";
                break;
        }
    }

    /* Custom Listener onPublisherDiscovery */
    void onPublisherDiscovery(
            eprosima::fastrtps::Participant * participant,
            eprosima::fastrtps::rtps::WriterDiscoveryInfo  && info) override
    {
        (void)participant;
        switch(info.status) {
            case eprosima::fastrtps::rtps::WriterDiscoveryInfo ::DISCOVERED_WRITER:
                /* Process the case when a new publisher was found in the domain */
                cout << "New publisher for topic '" << info.info.topicName() << "' of type '" << info.info.typeName() << "' discovered";
                break;
            case eprosima::fastrtps::rtps::WriterDiscoveryInfo ::CHANGED_QOS_WRITER:
                /* Process the case when a publisher changed its QOS */
                break;
            case eprosima::fastrtps::rtps::WriterDiscoveryInfo ::REMOVED_WRITER:
                /* Process the case when a publisher was removed from the domain */
                cout << "publisher for topic '" << info.info.topicName() << "' of type '" << info.info.typeName() << "' left the domain.";
                break;
        }
    }
};

// Create Custom user ParticipantListener (should inherit from eprosima::fastrtps::ParticipantListener.
CustomParticipantListener *listener = new CustomParticipantListener();
// Pass the listener on participant creation.
Participant* participant = Domain::createParticipant(participant_attr, listener);

 調優

1)利用多播。如果一個Topic有多個Subscriber,那麼選用多播是比較合適的,這樣,每個數據只發送一個網絡包,相較於單播,提高了CPU和網絡的使用率。

2)增大Socket緩衝區大小:在高速率、大數據的場景中,由於Socket緩衝區不足,可能會丟棄部分數據包。在可靠模式下,FastRTPS會試圖恢復這些丟失的數據包,這是有重傳代價的,而在高效模式下,這些丟失的數據包就徹底的丟失了。增加緩衝區大小從兩個地方增加:參考“如何提高發送大數據的性能?”第5步。

3)可靠模式優化:參考“如何提高發送大數據的性能?”第3步。

4)非嚴格可靠(topic.historyQos.kind):KEEP_ALL會讓所有的Subscriber必須接收所有的實例數據,在大量實例數據丟失的情況下,這會嚴重降低性能。如果用戶不需要這種嚴格性,可設置爲KEEP_LAST。

5)降低發送頻率:高頻率的發送數據會造成數據丟失。參考“如何提高發送大數據的性能?”第4步。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章