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