【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();
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();
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向网络发送消息)

注:在FastRTPS1.9中为remoteLocatorList

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发送自己的PDP消息,这个消息是周期性发送的,默认250s)

创建Participant时没有指定intial peers时,FastRTPS会将元多播定位器(Metatraffic Multicast Locators)作为网络发现的定位器,Participant会向这些地址发送自己的信息。

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发出的数据。


    
        HelloWorldSubscriber
        
            3
            4
            HelloWorldTopic
            HelloWorld
        
    

静态发现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步。

 

你可能感兴趣的:(C++,FastRTPS)