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