胡佳貝,邢 浩,呂睿娟
(中國航空工業(yè)集團公司西安航空計算技術研究所,陜西 西安 710068)
近年來,隨著分布式信息系統(tǒng)的快速發(fā)展,很多領域對于系統(tǒng)的實時性和可靠性提出了更高的要求,尤其是航空領域、作戰(zhàn)領域以及航海領域等。對于這些分布式硬實時系統(tǒng),如果數(shù)據(jù)的共享和傳遞不能按照預期到達,則會造成重大災難或事故。因此,對于實時數(shù)據(jù)分發(fā)系統(tǒng)的研究至關重要。傳統(tǒng)的客戶/服務器(Client/Server system,C/S)模式以對象為中心,由于其信息傳遞過程存在耦合度高、擴展性低、效率低等缺陷,已不足以滿足當前許多信息系統(tǒng)對于實時性的要求[1]。對象管理組織(Object Management Group,OMG)在以數(shù)據(jù)為中心的發(fā)布/訂閱(Data Centric Publish-Subscriber,DCPS)模型基礎上,制定了一套數(shù)據(jù)分發(fā)服務(Data Distribute Servic,DDS)標準[2]。該標準可以更好地滿足當前對于實時性要求高的系統(tǒng)的需求。
本文首先研究了兩種數(shù)據(jù)分發(fā)模型,其次在DDS開源實現(xiàn)OpenDDS的基礎上,實現(xiàn)了一個以數(shù)據(jù)為中心的實時分發(fā)系統(tǒng)。該系統(tǒng)可以作為分布式復雜實時分發(fā)系統(tǒng)的雛形,在此基礎上進行擴展所形成的復雜實時分發(fā)系統(tǒng)可以被廣泛應用到多個領域。
一般情況下,常用的數(shù)據(jù)分發(fā)模型可以分為傳統(tǒng)的C/S和DCPS兩種,具體內容如下[3]。
傳統(tǒng)的C/S通信模型如圖1所示,可以看到,該模型以處理對象為中心,通常情況下,由客戶端和服務器端建立鏈接,客戶端主動發(fā)起請求,服務器端處理請求并作出響應[4]。一個客戶端可以與多個服務器端進行交互,同時一個服務端可以為多個客戶端提供服務。常用的Web服務采用的就是C/S通信模型。
然而,從圖1中同樣也可以看出,客戶端和服務器端存在較高的耦合度,這樣會限制整個系統(tǒng)的靈活性和擴展性,而且該模型的通信過程較為復雜,使得信息的傳遞和共享效率會降低。如果在實時分布式系統(tǒng)中使用此模型,隨著運行時間的增加,一旦服務器端連接的客戶節(jié)點數(shù)目增加,就會增加服務器端的工作負荷,這樣服務器端很容易出現(xiàn)故障,最終造成整個系統(tǒng)無法運轉[5]。因此,該模型還存在單點失效問題,不適用于對實時性和可靠性要求較高的分布式信息系統(tǒng)中。
在OMG提出的DDS規(guī)范中,將DDS API的接口進行了分層,分別為數(shù)據(jù)本地重構層(Data Local Reconstruction Layer,DLRL) 和 DCPS[6]。 其 中,DLRL位于DCPS的上層,對于DCPS提供的服務進行了映射和封裝,這樣便于上層應用程序去使用。
實際上,DDS規(guī)范中最重要的內容是DCPS。該模型是一個與平臺無關的數(shù)據(jù)模型,其通信模型如圖2所示[7]。主要功能是將發(fā)布端發(fā)送的信息高效地傳遞給對該信息感興趣的訂閱端。DCPS構建了一個全局數(shù)據(jù)空間(Global Data Space,GDS),對于每個分布式應用節(jié)點,既可以作為發(fā)布者角色往GDS中寫數(shù)據(jù),也可以作為訂閱者角色讀取GDS中感興趣的數(shù)據(jù),同時也可以兩種角色兼得[8]。每當發(fā)布者將數(shù)據(jù)發(fā)送到GDS時,DDS中間件就會快速地將數(shù)據(jù)傳遞給對該數(shù)據(jù)感興趣的所有訂閱者[9]。因此,發(fā)布者與訂閱者就不需要知道彼此的存在,這便形成了一種低耦合的連接方式,而且任何分布式應用節(jié)點可以隨時接入到GDS中,從而增加了整個系統(tǒng)的靈活性和擴展性。
DDS規(guī)范中包括的實體對象有域參與者(Domain Participant)、主題(Topic)、發(fā)布者(Publisher)、訂閱者(Subscriber)、數(shù)據(jù)讀者(DataReader)以及數(shù)據(jù)寫者(DataWriter),彼此之間的關系如圖3所示。
DDS通過引入域的概念將通信空間劃分為不同的平面,處于同一域或者同一個平面內的實體之間才能相互通信,處于不同域或不同平面內的實體之間不能通信。每個域中可以包含多個域參與者,每個域參與者也可以包含多個發(fā)布者或多個訂閱者,每個發(fā)布者可以包含多個數(shù)據(jù)寫者,每個訂閱者也可以包含多個數(shù)據(jù)讀者[10]。主題作為發(fā)布者和訂閱者進行通信的橋梁,并且每個主題關聯(lián)特定的數(shù)據(jù)類型。在每一次的發(fā)布訂閱過程中,由DDS中間件通過檢測發(fā)布者和訂閱者之間的主題是否相匹配,如果匹配,則建立通信鏈接,反之不會建立鏈接。同時,在整個DDS的通信過程中,可以根據(jù)需要通過相關的QoS來設置數(shù)據(jù)傳輸過程中的質量。因此,DPCS保證了正確的數(shù)據(jù)在正確的時間內到達正確的地方。
以DDS的開源實現(xiàn)OpenDDS為例,在不同操作系統(tǒng)之間設計并實現(xiàn)了一個簡單的實時發(fā)布系統(tǒng),設計思路如圖4所示。
2.1.1 創(chuàng)建域參與者Participant對象
發(fā)布端應用程序首先需要創(chuàng)建一個域參與者工廠對象dpf,由域參與者工廠對象dpf調用create_participant()方法創(chuàng)建域參與者對象participant。在本示例中,設置的域ID值為10,所有的DDS實體對象均采用默認的QoS,具體代碼為:
DDS::DomainParticipantFactory_var dpf = ThePartici pantFactoryWithArgs(argc, argv);
DDS::DomainParticipant_var participant=dpf->create_participant(10, PARTICIPANT_QOS_DEFAULT,0, OpenDDS::DCPS::DEFAULT_STATUS_MASK);
2.1.2 注冊發(fā)布的數(shù)據(jù)類型
接著采用register_type()方法注冊要發(fā)布的數(shù)據(jù)類型。此類型是事先定義好的數(shù)據(jù)結構,具體代碼為:
Messenger::MessageTypeSupport_var ts =new Messen ger::MessageTypeSupportImpl;
ts->register_type(participant, “Message”);
2.1.3 創(chuàng)建主題Topic對象
利用域參與者對象participant調用create_topic()方法來創(chuàng)建要發(fā)布的主題對象topic,主題對象topic需要與主題名稱、數(shù)據(jù)類型進行關聯(lián),具體代碼為:
DDS::Topic_var topic =participant->create_topic(“Movie List”,”Message”, TOPIC_QOS_DEFAULT,
0, OpenDDS::DCPS::DEFAULT_STATUS_MASK);
2.1.4 創(chuàng)建發(fā)布者Publisher對象和數(shù)據(jù)寫者Writer對象
利用域參與者對象participant調用create_publisher()方法創(chuàng)建發(fā)布者對象publisher,由發(fā)布者對象publisher調用create_datawriter()方法創(chuàng)建相關的寫者對象writer,該對象和之前創(chuàng)建的topic對象進行關聯(lián),具體代碼為:
DDS::Publisher_var publisher =participant->create_publisher(PUBLISHER_QOS_DEFAULT, 0,OpenDDS::DCPS::DEFAULT_STATUS_MASK);
DDS::DataWriter_var writer =publisher->create_datawriter(topic, DATAWRITER_QOS_DEFAULT, 0,OpenDDS::DCPS::DEFAULT_STATUS_MASK);
Messenger::MessageDataWriter_var message_writer=Messenger::MessageDataWriter::_narrow(writer);
2.2.1 創(chuàng)建域參與者Participant對象
訂閱端應用程序首先也需要創(chuàng)建一個域參與者工程對象dpf,由域參與者工廠對象dpf調用create_participant()方法創(chuàng)建域參與者對象participant。在本示例中,設置的域ID值為10,所有的DDS實體對象均采用默認的QoS,具體代碼為:
DDS::DomainParticipantFactory_var dpf = ThePartici pantFactoryWithArgs(argc, argv);
DDS::DomainParticipant_var participant=dpf->create_participant(10, PARTICIPANT_QOS_DEFAULT,0, OpenDDS::DCPS::DEFAULT_STATUS_MASK);
2.2.2 注冊訂閱的數(shù)據(jù)類型
接著采用register_type()方法注冊要訂閱的數(shù)據(jù)類型。此類型是事先定義好的數(shù)據(jù)結構,具體代碼為:
Messenger::MessageTypeSupport_var ts =new Messen ger::MessageTypeSupportImpl;
ts->register_type(participant, “Message”);
2.2.3 創(chuàng)建主題Topic對象
利用域參與者對象participant調用create_topic()方法來創(chuàng)建要發(fā)布的主題對象topic,主題對象topic需要與主題名稱、數(shù)據(jù)類型進行關聯(lián),具體代碼為:
p a r t i c i p a n t->c r e a t e_t o p i c(“M o v i e List”,”Message”, TOPIC_QOS_DEFAULT, 0,OpenDDS::DCPS::DEFAULT_STATUS_MASK);
2.2.4 創(chuàng)建訂閱者Subscriber對象
利用域參與者對象participant調用create_subscriber ()方法創(chuàng)建訂閱者對象subscriber,具體代碼為:
DDS::Subscriber_var subscriber =participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT, 0,OpenDDS::DCPS::DEFAULT_STATUS_MASK);
2.2.5 創(chuàng)建監(jiān)聽器Listener對象和數(shù)據(jù)讀者DataReader對象
由訂閱者對象subscriber調用create_datareader()方法創(chuàng)建相關的寫者對象reader,該對象和之前創(chuàng)建的topic對象進行關聯(lián),同時,在創(chuàng)建寫者對象reader對象的需要綁定一個監(jiān)聽器對象listener,以便于對發(fā)布端發(fā)布的數(shù)據(jù)進行異步監(jiān)聽和檢測,具體代碼如下:
DDS::DataReaderListener_var listener(new DataReaderListenerImpl);
DDS::DataReader_var reader =subscriber->create_datareader(topic, reader_qos, listener,OpenDDS::DCPS::DEFAULT_STATUS_MASK);
Messenger::MessageDataReader_var message_reader=Messenger::MessageDataReader::_narrow(reader);
本文首先將傳統(tǒng)C/S模型和DCPS模型進行對比,前者具有較高的耦合度與擴展性低等缺點,而DDS規(guī)范中的DCPS模型具有耦合度低和靈活性高等優(yōu)勢,可以很好地滿足目前分布式系統(tǒng)對實時性與可靠性的需求。其次基于DDS的開源實現(xiàn)OpenDDS,設計并實現(xiàn)了一個簡單的實時分發(fā)系統(tǒng),介紹了應用程序的開發(fā)過程。后續(xù)可以在此基礎上進一步擴展,將其應用到更多的領域及更復雜的場景。