【FastRTPS】Publisher-Subscriber接口層、使用及部分配置

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

參考《FastRTPS User Manual.odt》第五章

Publisher-Subscriber接口層

eProsima Fast RTPS 提供了高層的Publisher-Subscriber層,該層是RTPS協議上的簡單抽象。通過這層,你可以直接編寫程序代碼而不用管低層RTPS配置,因爲這是由庫自己管理的。(底層RTPS有很多配置選項,可以改善此層的應用效果

類介紹

Domain

創建Participant、Subscriber、Publisher的管理類,以及註冊在網絡上使用的數據類型和話題。

自定義話題類型例子https://blog.csdn.net/JL_Gao/article/details/85685086

Participant 

管理Publisher和Subscriber的分組類,保存了Publisher、Subscriber、TopicDataType等數據。

ParticipantAttributes

創建Participant配置選項,有一些參數是必須指定的:DomainId Discovery

DomainId:用於計算PDP發現端口,非常適用於同一個網絡中有多個應用程序的情況,這樣就能將網絡中的程序分離。對於同一個計算機上同樣的DomainId的Participant來說,發送接收PDP消息使用的多播端口是一樣的,單播端口與ParticipantID有關,所以每個Participant用到的單播端口是不一樣的。對於網絡中不同的計算機來說,只要Participant的DomainId一樣,就可以通過同一個多播地址來發現彼此,DomainId不同的Participant,他們發送接收PDP消息的多播地址端口不一樣,也就不會彼此發現了。

Discovery:如果使用靜態發現(SEDP),需要提供相應的XML端點配置文件。

創建Participant的例子

ParticipantAttributes PParam;
Pparam.rtps.setName("participant");
Pparam.rtps.builtin.domainId = 80;
Pparam.rtps.listenSocketBufferSize = 100000; 

Participant* p = Domain::createParticipant(PParam);
if(p!=nullptr){
//Participant correctly created
}
//To remove:
Domain::removeParticipant(p);

從Participant修改RTPS底層網絡配置:

ParticipantAttributes Pparams;
auto myTransport = std::make_shared<UDPv6Transport::TransportDescriptor>();
myTransport->receiveBufferSize = 65536;
myTransport->granularMode = false;
Pparams.rtps.useBuiltinTransport = false;
Pparams.rtps.userTransports.push_back(myTransport);

Publisher

用於向網絡中的Subscriber發送數據。用戶通過Publisher發送數據,也可以通過PublisherListener處理一些事件。

自定義Publisher例子https://blog.csdn.net/JL_Gao/article/details/85694276

Domain創建Publisher的驗證過程

1. 如果Participant使用的SEDP靜態端點發現協議,則必須定義userDefinedId,且>0

2. 使用的數據類型必須提前註冊

3. 如果topic類型是WITH_KEY,註冊的數據類型必須實現getKey方法。

數據包拆分:

發送緩衝區大小的參數是從Participant繼承的,默認爲65KB。當RTPS消息很大不能一次發送時,該消息將會被分割。分割後的消息會通過多個數據包發送,此時需將 qos.m_publishMode 設置爲異步。

在高效模式下,數據包的大小正確就意味着數據包被正確接收了。

// Allows fragmentation.
publisher_attr.qos.m_publishMode.kind = ASYNCHRONOUS_PUBLISH_MODE;

流控制策略:

用戶可配置流控制策略,用來限制在特定條件下發送的數據量(可用不同的流控制器類型實現)

啓用內置吞吐量控制器:

// This controller allows 300kb per second.
ThroughputControllerDescriptor slowPublisherThroughputController{300000, 1000};

PublisherAttributes WparamSlow;
WparamSlow.terminalThroughputController = slowPublisherThroughputController;

mp_slow_publisher = Domain::createPublisher(mp_participant,WparamSlow,(PublisherListener*)&m_listener);

PublisherListener

實現的方法應避免耗時循環和阻塞語句,以防引起事件或偵聽線程阻塞。

Subscriber

自定義Subscriber例子:https://blog.csdn.net/JL_Gao/article/details/85694276

數據包拆分支持:能夠接收單個重量級消息的多個數據包

1. 利用Writer的流控制,使Reader能夠跟上發送的帶寬

2. 利用可靠QoS,當Reader無法跟上發送帶寬時,讓丟失的分片重新發送。

QoS

FastRTPS對標準的QoS提供了部分本地支持,此外,不能通過API訪問的QoS類型可以在用戶端實現。

SampleInfo_t:從History中讀取或獲取數據時提供的輔助結構,用在takeNextData 和 readNextData

    sampleKind:ALIVE(活動的)、DISPOSED(已處理的)、UNREGISTERED(未註冊的)

    ownershipStrength:接收到數據時,Writer的所有權強度

    sourceTimestamp:數據發送的時間戳,可用於實現基於時間的過濾

    sample_identity:Writer的GUID、數據的序號

MatchingInfo:兩個端點間的匹配信息,用在onPublicationMatched 和 onSubscriptionMatched

    remoteEndpointGuid:匹配的Writer或Reader的GUID

    status:MATCHED_MATCHING、REMOVED_MATCHING

基於時間的過濾和基於內容的過濾Qos例子https://blog.csdn.net/JL_Gao/article/details/85700559

所有權Qos例子:https://blog.csdn.net/JL_Gao/article/details/85700559

期限和所有權強度QoS例子:參考源碼中example

 

如何使用Publisher-Subscriber層

第一步:創建Participant對象,相當於我們程序中用到的Publisher與Subscriber的容器。

ParticipantAttributes participant_attr; //Configuration structure
Participant *participant = Domain::createParticipant(participant_attr);

Domain -- 創建Participant、Subscriber、Publisher的管理類,以及註冊在網絡上使用的數據類型和話題。

Participant -- 管理Publisher和Subscriber的分組類,保存了Publisher、Subscriber、TopicDataType等數據。

ParticipantAttributes -- Participant配置,上面代碼中採用的默認配置。默認配置提供了一些基本選項包括通信用到的端口。

第二步:註冊Topic

HelloWorldPubSubType m_type; //Auto-generated type from FastRTPSGen
Domain::registerType(participant, &m_type);

HelloWorldPubSubType -- 由fastrtpsgen生成的類

第三步:創建Publisher對象

PublisherAttributes publisher_attr; //Configuration structure
PubListener m_listener; //Class that implements callbacks from the publisher
Publisher *publisher = Domain::createPublisher(participant, publisher_attr, (PublisherListener *)&m_listener);

PubListener -- 實現回調函數

Publisher 有一組可選的回調函數,它們會中事件發生時觸發,例如當Subscriber開始偵聽同一個Topic。可通過繼承PublisherListener實現對不同事件的處理。

class PubListener : public PublisherListener
{
    public PubListener(){};
    ~PubListener(){};
    void onPublicationmatched(Publisher* pub, MatchingInfo& info)
    {
        //Callback implementation. This is called each time the Publisher finds a Subscriber on the network that listens to the same topic.
    }
} m_listener;

第四步:發佈數據

HelloWorld m_Hello; //Auto-generated container class for topic data from FastRTPSGen
m_Hello.msg("Hello there!"); // Add contents to the message
publisher->write((void *)&m_Hello); //Publish

第五步:創建Subscriber對象

SubscriberAttributes subscriber_attr; //Configuration structure
SubListener m_listener; //Class that implements callbacks from the Subscriber
Subscriber *subscriber = Domain::createSubscriber(participant,subscriber_attr,(SubsciberListener*)&m_listener);

SubListener類 -- 實現回調函數(實現同PubListener)

 

配置

通過參數sendSocketBufferSize和listenSocketBufferSize可配置底層UDP套接字的發送和接收緩衝區大小。因爲當要發送的數據類型大於底層發送緩衝區時,FastRTPS會將數據拆分成多個數據碎片,並在接收端重新組合構建。

Participant 配置

代碼方式:

ParticipantAttributes participant_attr;
participant_attr.setName("my_participant");
participant_attr.rtps.builtin.domainId = 80;
Participant *participant = Domain::createParticipant(participant_attr);

XML文件方式:

Participant *participant = Domain::createParticipant("participant_xml_profile");
<profiles>
    <participant profile_name="participant_xml_profile">
        <rtps>
            <name>my_participant</name>
            <builtin>
                <domainId>80</domainId>
            </builtin>
        </rtps>
    </participant>
</profiles>

配置項:

setName:設置Participant Name,這是RTPS協議元數據中一部分。

rtps.builtin.domainId:域ID,一般用於區分不同應用。

Publisher和Subscriber 配置

代碼方式:

PublisherAttributes publisher_attr;
publisher_attr.topic.topicDataType = "HelloWorldType";
publisher_attr.topic.topicName = "HelloWorldTopic";
publisher_attr.qos.m_reliability.kind = RELIABLE_RELIABILITY_QOS;
publisher_attr.topic.historyQos.kind =KEEP_LAST_HISTORY_QOS;
publisher_attr.topic.historyQos.depth = 5
publisher_attr.qos.m_durability.kind =TRANSIENT_LOCAL_DURABILITY_QOS;
publisher_attr.topic.resourceLimitsQos.max_samples = 200;
Locator_t unicast_locator;
unicast_locator.port = 7800;
publisher_attr.unicastLocatorList.push_back(unicast_locator);
Locator_t multicast_locator;
multicast_locator.set_IP4_address("239.255.0.4");
multicast_locator.port = 7900;
publisher_attr.multicastLocatorList.push_back(multicast_locator);
Publisher *publisher = Domain::createPublisher(participant, publisher_attr);

SubscriberAttributes subscriber_attr;
...
Subscriber *subscriber = Domain::createSubscriber(participant, subscriber_attr);

XML方式:

Publisher *publisher = Domain::createPublisher(participant, "publisher_xml_profile");
Subscriber *subscriber = Domain::createSubscriber(participant, "subscriber_xml_profile");
<profiles>
   <publisher profile_name="publisher_xml_profile">
      <topic>
         <dataType>HelloWorldType</dataType>
         <name>HelloWorldTopic</name>
         <historyQos>
            <kind>KEEP_LAST</kind>
            <depth>5</depth>
         </historyQos>
         <resourceLimitsQos>
            <max_samples>200</max_samples>
         </resourceLimitsQos>
      </topic>
      <qos>
         <reliability>
            <kind>RELIABLE</kind>
         </reliability>
         <durability>
            <kind>TRANSIENT_LOCAL</kind>
         </durability>
      </qos>
      <unicastLocatorList>
         <locator>
            <port>7800</port>
         </locator>
      </unicastLocatorList>
      <multicastLocatorList>
         <locator>
            <address>239.255.0.4</address>
            <port>7900</port>
         </locator>
      </multicastLocatorList>
   </publisher>

   <subscriber profile_name="subscriber_xml_profile">
      ...
   </subscriber>
</profiles>

配置項(藍色字體是代碼中的配置值,藍色字體後面的是XML文件中的配置值):

topic.topicDataType:Topic數據類型,

topic.topicName:Topic名稱,

topic.topicKind:WITH_KEY、NO_KEY(默認)

topic.resourceLimitsQos.max_samples:History的最大存儲大小 

topic.resourceLimitsQos.max_instance:(Subscriber) 最多接收多少個關鍵字的數據

topic.resourceLimitsQos.max_samples_per_instance:(Subscriber)每個關鍵字的能接收的最大大小

注意:max_samples 必須大於max_samples_per_instance

topic.historyQos:RTPS底層History元素配置參數訪問點

topic.historyQos.kind:緩存策略,KEEP_ALL、KEEP_LAST(默認)

    KEEP_ALL_HISTORY_QOS(KEEP_ALL)-- 保存所有的Change數據

    KEEP_LAST_HISTORY_QOS(KEEP_LAST) -- 當數據條數大於depth時,保存最新的Change數據,並覆蓋舊數據

qos.m_publishMode:ASYNCHRONOUS_PUBLISH_MODE

qos.m_ownership.kind:EXCLUSIVE_OWNERSHIP_QOS(只能有一個Writer能更新實例,該Writer擁有最大所有權)

qos.m_reliability.kind:可靠性,默認BEST_EFFORT

    BEST_EFFORT_RELIABILITY_QOS (BEST_EFFORT)-- 不需要接收者回復確認,較快,但可能有數據丟失。

    RELIABLE_RELIABILITY_QOS (RELIABLE)-- 需要接收者回復確認,較慢,能防止數據丟失。

    !!!注意配置的兼容性!!!

 

Publisher \ Subscriber

Best Effort Reliable
Best Effort
Reliable

qos.m_durability.kind:持久性,定義了在subscriber加入前,對topic上數據的存儲行爲。

    publisher默認TRANSIENT_LOCAL,subscriber默認VOLATILE

    VOLATILE_DURABILITY_QOS(VOLATILE)--  忽略subscriber matched之前的數據  

    TRANSIENT_LOCAL_DURABILITY_QOS(TRANSIENT_LOCAL)-- 有新的subscriber時,會將過去的數據添加到History

    TRANSIENT_DURABILITY_QOS(不瞭解)-- 有新的subscriber時,會將持久存儲中的數據添加到History

times.heartbeatPeriod:心跳週期,用seconds和fraction字段設置秒和毫秒,默認3s。主要用在底層StatefulWriter上面,用來週期性的檢查對方有沒有收到數據。減少心跳週期能提高不穩定網絡情況下的性能。

times.nackResponseDelay:返回ACKNACK消息前的延遲,主要用在底層StatefulWriter上面,用來向對方發送沒有收到的數據。

unicastLocator:單播定位器,定義接收數據的網絡端點(參考網絡配置)。Publisher和Subscriber會從Participant中繼承單播定位器,也可以通過該配置項配置不同的定位器。配置後會覆蓋Participant的默認配置。

multicastLocator:多播定位器,默認情況下Publisher和Subscriber都不會使用多播定位器,但是當有很多publisher/subscriber ,它們之間通過單播是每次都複製一份再發送,這樣就降低了publisher端的效率,所以此時採用多播能夠有效降低網絡使用率。配置後會覆蓋Participant的默認配置。

eProsima Fast RTPS 中的locator(定位器)就是網絡端點。

Locator定義:

class RTPS_DllAPI Locator_t
{
    public:
        int32_t kind;     // 協議,LOCATOR_KIND_UDPv4/LOCATOR_KIND_UDPv6
        uint32_t port;
        octet address[16]; // IP address
}

 

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