曹建平,朱國(guó)濤,孫文柱,胡文婷
(海軍航空大學(xué) 青島校區(qū),山東 青島 266000)
為提高性能和費(fèi)效比,大型復(fù)雜計(jì)算常常采用分布式方式進(jìn)行[1]。分布式系統(tǒng)作為一個(gè)體系,一般包括多個(gè)異構(gòu)的子系統(tǒng),各子系統(tǒng)的多樣性、異構(gòu)性為集成工作帶來很大挑戰(zhàn),因此建立統(tǒng)一的系統(tǒng)集成框架具有重要意義。該框架不但要保證系統(tǒng)集成達(dá)到低耦合、擴(kuò)展性、易配置和實(shí)時(shí)性等要求,還要有效提高開發(fā)、升級(jí)和維護(hù)效率。
當(dāng)前,對(duì)于分布式異構(gòu)系統(tǒng)的集成方法和框架包括:應(yīng)用于分布式仿真的分布式交互仿真(distributed interactive simulation,DIS)框架和高層體系結(jié)構(gòu)(high level architecture,HLA)框架,目前已形成IEEE標(biāo)準(zhǔn)[2],并有多種軟件實(shí)現(xiàn),在軍事仿真領(lǐng)域應(yīng)用廣泛;基于XML、SOAP和WSDL技術(shù)的Web Service框架[3],可實(shí)現(xiàn)異構(gòu)的分布式web應(yīng)用進(jìn)行實(shí)時(shí)交互,在軟件實(shí)現(xiàn)上包括微軟.NET,IBM的WebSphere和Borland的JBuilder等,在電子商務(wù)、電子政務(wù)和游戲領(lǐng)域應(yīng)用廣泛;以數(shù)據(jù)為中心,采用虛擬總線的“發(fā)布-訂閱”通信模式,強(qiáng)調(diào)高可靠性和實(shí)時(shí)性的數(shù)據(jù)分發(fā)服務(wù)(Data Distribution Service)框架[4],通過21種QoS服務(wù)質(zhì)量策略能很好支持異構(gòu)設(shè)備之間數(shù)據(jù)分發(fā)和傳輸,包括OpenDDS、OpenSpliceDDS等軟件實(shí)現(xiàn),廣泛應(yīng)用于國(guó)防、民航和工業(yè)控制領(lǐng)域;在物聯(lián)網(wǎng)領(lǐng)域還包括AMOP、XMPP、MQTT和COAP等框架和實(shí)現(xiàn)。Michael等為集成多種異構(gòu)模擬器提供了一種名為Ptolemy II的集成環(huán)境;Silva T W B[5]等針對(duì)異構(gòu)的硬件平臺(tái)環(huán)境,基于HLA協(xié)議,在聯(lián)邦大使和應(yīng)用程序之間提出一種通用的松耦合的虛擬總線(Virtual Bus)計(jì)算平臺(tái),達(dá)到了提高集成效率的目的;朱曉攀[6]等根據(jù)像質(zhì)處理提升仿真系統(tǒng)需求提出了采用數(shù)據(jù)分發(fā)服務(wù)(data distributed service,DDS)技術(shù)進(jìn)行集成,實(shí)現(xiàn)海量圖像和數(shù)據(jù)傳輸?shù)陌葱璺峙洹?shí)時(shí)性、可靠性、擴(kuò)展性和高吞吐率要求。
以上框架和相應(yīng)的實(shí)現(xiàn)在各自領(lǐng)域都達(dá)到了系統(tǒng)集成框架的功能和性能要求,但在細(xì)分領(lǐng)域還需要進(jìn)行定制開發(fā),存在一定的開發(fā)和維護(hù)難度。為此,針對(duì)分布式飛行仿真提出了一種新的分布式異構(gòu)系統(tǒng)集成方法和框架,并利用C++原生語言進(jìn)行了實(shí)現(xiàn)。該框架在某型飛機(jī)分隊(duì)?wèi)?zhàn)術(shù)模擬訓(xùn)練中得到了應(yīng)用,應(yīng)用結(jié)果表明該框架可用于互聯(lián)包括視景顯示計(jì)算機(jī)、飛行控制計(jì)算機(jī)、教員控制臺(tái)、計(jì)算機(jī)生成兵力等多種異構(gòu)實(shí)體,具有低耦合、擴(kuò)展性強(qiáng)、易配置和實(shí)時(shí)性高的優(yōu)點(diǎn)。該框架還可應(yīng)用于其他類似的輕量級(jí)分布式實(shí)時(shí)應(yīng)用的異構(gòu)集成。
對(duì)于大型的分布式系統(tǒng),系統(tǒng)之間的關(guān)系比較復(fù)雜,傳統(tǒng)一般采取如圖1所示的緊耦合方式進(jìn)行集成。這種設(shè)計(jì)對(duì)系統(tǒng)的開發(fā)、升級(jí)和擴(kuò)展都不利,尤其在增加新的系統(tǒng)或數(shù)據(jù)時(shí)(圖1中的虛線)時(shí)非常困難,隨著系統(tǒng)復(fù)雜度的增加甚至?xí)斐烧麄€(gè)分布式系統(tǒng)的崩潰。因此,采取松散耦合的方式進(jìn)行系統(tǒng)集成已經(jīng)成為共識(shí)。
圖1 傳統(tǒng)設(shè)計(jì)的緊耦合網(wǎng)絡(luò)
為破除系統(tǒng)之間的緊耦合關(guān)系,在系統(tǒng)之間添加一個(gè)分發(fā)服務(wù)器,每個(gè)系統(tǒng)通過分發(fā)服務(wù)器進(jìn)行交互,如圖2所示。該設(shè)計(jì)的基本思想就是將各系統(tǒng)的數(shù)據(jù)集中到分發(fā)服務(wù)器上,同時(shí)分發(fā)服務(wù)器又實(shí)時(shí)地將數(shù)據(jù)分發(fā)給各子系統(tǒng),從而達(dá)到了分布式系統(tǒng)數(shù)據(jù)在各子系統(tǒng)間的共享。各子系統(tǒng)都包含了一個(gè)解析子模塊,負(fù)責(zé)對(duì)共享的數(shù)據(jù)進(jìn)行過濾和解析,這樣各子系統(tǒng)在升級(jí)或添加新的子系統(tǒng)時(shí)只需要更新解析子模塊即可,從而達(dá)到了系統(tǒng)之間的松耦合,將這種數(shù)據(jù)集成方式稱為“集中-分發(fā)”模式。
該設(shè)計(jì)中的分發(fā)服務(wù)器與DDS技術(shù)中的“虛擬總線”概念有所類似,都是為了達(dá)到子系統(tǒng)間數(shù)據(jù)共享的目的,但實(shí)際上是有區(qū)別的,主要體現(xiàn)為兩點(diǎn):一是DDS中的“總線”是虛擬的,實(shí)際上是通過網(wǎng)絡(luò)中間層實(shí)現(xiàn)的,而本設(shè)計(jì)是存在一個(gè)確定且獨(dú)立運(yùn)行的分發(fā)服務(wù)器;二是DDS的“虛擬總線”核心是“發(fā)布-訂閱”模式,而本設(shè)計(jì)是將分布式系統(tǒng)中的所有數(shù)據(jù)送到各子系統(tǒng),由各子系統(tǒng)過濾解析獲取需要的數(shù)據(jù)。之所以采取這種方式還是由于面向的是輕量級(jí)應(yīng)用,雖然通過“發(fā)布-訂閱”方式可減少數(shù)據(jù)冗余,但對(duì)數(shù)據(jù)傳輸效率帶來不良影響且對(duì)系統(tǒng)的可擴(kuò)展也不利。
圖2 基于“集中-分發(fā)”模式的松散耦合設(shè)計(jì)
在該模式中,分發(fā)服務(wù)器處于核心位置,要實(shí)現(xiàn)分發(fā)服務(wù)器與所有子系統(tǒng)進(jìn)行交互要求分發(fā)服務(wù)器功能單一,即只完成數(shù)據(jù)的分發(fā),不涉及到數(shù)據(jù)的具體內(nèi)容,分發(fā)服務(wù)器與各子系統(tǒng)之間不存在任何額外的協(xié)議。因此,對(duì)于各子系統(tǒng)而言,分發(fā)服務(wù)器實(shí)際上相當(dāng)于一個(gè)快遞公司,只負(fù)責(zé)郵件(數(shù)據(jù))的托運(yùn)而沒有權(quán)限查看或處理郵件的內(nèi)容。這是達(dá)到松散耦合的一個(gè)必要條件,但不是充分條件。原因在于,雖然分發(fā)器對(duì)傳輸數(shù)據(jù)的內(nèi)容是無關(guān)的,但卻對(duì)傳輸數(shù)據(jù)的大小是敏感的,因此需對(duì)分布式系統(tǒng)的單次傳輸量進(jìn)行統(tǒng)一規(guī)定。這樣,分發(fā)服務(wù)器就成為了一個(gè)特殊的快遞公司,它只接收具有固定大小的郵件并將其發(fā)送到所有想接收這個(gè)郵件的客戶(各子系統(tǒng))手里。對(duì)于一個(gè)子系統(tǒng)而言,需將其單位業(yè)務(wù)數(shù)據(jù)包的大小包裝成分發(fā)器傳輸規(guī)定的大小。要完成松散耦合,另一個(gè)必要條件是各子系統(tǒng)能正確過濾和解析從分發(fā)器發(fā)送過來的數(shù)據(jù)。為實(shí)現(xiàn)該功能,在分發(fā)服務(wù)器中設(shè)置一個(gè)緩沖區(qū)域,在該區(qū)域中各子系統(tǒng)數(shù)據(jù)的相對(duì)位置是固定的,對(duì)各子系統(tǒng)進(jìn)行編號(hào),將編號(hào)和位置建立一一映射。這樣,某一個(gè)子系統(tǒng)就可以通過編號(hào)設(shè)置接收哪些子系統(tǒng)的數(shù)據(jù),并根據(jù)相對(duì)位置找到對(duì)應(yīng)的數(shù)據(jù)并進(jìn)行解析。該過程通過配置文件實(shí)現(xiàn),為系統(tǒng)的可擴(kuò)展提供了可能。另外,分發(fā)服務(wù)器緩沖區(qū)域在場(chǎng)景運(yùn)行之前就是固定好不能更改的,分發(fā)服務(wù)器中的接收和發(fā)送線程通信采用“best-effort”模式,只要緩沖區(qū)域內(nèi)有數(shù)據(jù)就發(fā)送,從而可以適應(yīng)不同子系統(tǒng)的發(fā)送接收數(shù)據(jù)頻率的差異性。
為此,在分發(fā)服務(wù)器和子系統(tǒng)之間還需要設(shè)置一個(gè)中間件完成上述工作。該中間件具有3個(gè)作用:1)將本地單位業(yè)務(wù)數(shù)據(jù)包裝成符合傳輸要求的數(shù)據(jù);2)過濾和解析遠(yuǎn)程子系統(tǒng)數(shù)據(jù);3)進(jìn)行數(shù)據(jù)傳輸。如圖3所示,根據(jù)中間件功能,中間件可劃分為3個(gè)模塊,分別是數(shù)據(jù)打包模塊、數(shù)據(jù)解析模塊和數(shù)據(jù)傳輸模塊。整個(gè)數(shù)據(jù)流過程是:打包模塊負(fù)責(zé)將子系統(tǒng)的數(shù)據(jù)打包成符合分發(fā)服務(wù)器規(guī)定大小的數(shù)據(jù)包并通過傳輸模塊中的發(fā)送線程將本地?cái)?shù)據(jù)發(fā)送到分發(fā)服務(wù)器;而傳輸模塊中的接收線程負(fù)責(zé)接收分發(fā)服務(wù)器發(fā)送的整個(gè)分布式系統(tǒng)的共享數(shù)據(jù),并由解析模塊對(duì)共享數(shù)據(jù)進(jìn)行過濾和解析。中間件可以以多種方式集成到子系統(tǒng)中,既可以打包成動(dòng)態(tài)鏈接庫(kù)由子系統(tǒng)調(diào)用,也可以是一個(gè)單獨(dú)的進(jìn)程與子系統(tǒng)進(jìn)行數(shù)據(jù)交互。
圖3 中間件的模塊劃分與數(shù)據(jù)流
由上可知,分發(fā)服務(wù)器是業(yè)務(wù)無關(guān)的,而子系統(tǒng)是通過中間件來分享分布式系統(tǒng)的數(shù)據(jù),因此當(dāng)一個(gè)新的子系統(tǒng)加入分布式系統(tǒng)時(shí)只需要定制中間件中的解析模塊即可實(shí)現(xiàn),從而實(shí)現(xiàn)了子系統(tǒng)間數(shù)據(jù)集成的松散耦合。
面向輕量級(jí)的分布式實(shí)時(shí)應(yīng)用,根據(jù)基于“集中-分發(fā)”模式的異構(gòu)數(shù)據(jù)集成方法,實(shí)現(xiàn)了數(shù)據(jù)集成框架,總體框架如圖4所示。在該框架下,將分布式應(yīng)用的整個(gè)運(yùn)行過程稱為場(chǎng)景,將場(chǎng)景中的每個(gè)子系統(tǒng)稱為參與者,每個(gè)參與者有一個(gè)角色屬性(參與者括號(hào)的內(nèi)容),根據(jù)角色屬性可確定其業(yè)務(wù)數(shù)據(jù)類型。根據(jù)數(shù)據(jù)流方向,將框架分為3個(gè)部分:由參與者組成的參與者集、分發(fā)服務(wù)器、嵌入了中間件的中間層。原生的中間件是一個(gè)動(dòng)態(tài)鏈接庫(kù),可以采用多種方式與參與者集成,圖4給出了常見的4種方式:1)參與者程序直接調(diào)用中間件動(dòng)態(tài)庫(kù);2)將中間件動(dòng)態(tài)庫(kù)封裝成為一個(gè)獨(dú)立的程序,通過網(wǎng)絡(luò)與參與者進(jìn)行通信;3)參與者基于高層體系結(jié)構(gòu)(HLA)開發(fā),則將中間件封裝成為一個(gè)聯(lián)邦成員,通過HLA/RTI通信;4)參與者是一種多Agent體系結(jié)構(gòu),則可將中間件封裝成為一個(gè)Agent,通過多Agent系統(tǒng)中的通用黑板(GBB)進(jìn)行通信。
分發(fā)服務(wù)器則有兩部分組成,分別是分發(fā)模塊和緩沖區(qū)。每一個(gè)參與者按順序?qū)?yīng)緩沖區(qū)的一個(gè)子區(qū)域,每個(gè)子區(qū)域存儲(chǔ)對(duì)應(yīng)參與者的實(shí)時(shí)狀態(tài)。每個(gè)子區(qū)域又對(duì)應(yīng)分發(fā)模塊中的IO線程,IO線程負(fù)責(zé)與對(duì)應(yīng)的中間件進(jìn)行網(wǎng)絡(luò)通信,從而更新緩沖區(qū)子區(qū)域中的參與者實(shí)時(shí)狀態(tài)。
場(chǎng)景中的參與者對(duì)數(shù)據(jù)需求是不同的,有的只生產(chǎn)數(shù)據(jù),有的只消費(fèi)數(shù)據(jù),有的既生產(chǎn)也消費(fèi)數(shù)據(jù)。因此,在分發(fā)服務(wù)器中分發(fā)模塊分為只寫模式、只讀模式、讀寫模式,只寫模式對(duì)應(yīng)一個(gè)寫線程,只讀模式對(duì)應(yīng)一個(gè)讀線程,讀寫模式對(duì)應(yīng)一個(gè)寫線程和一個(gè)讀線程。寫線程從中間件讀取參數(shù)者數(shù)據(jù)并存儲(chǔ)到緩沖區(qū)對(duì)應(yīng)子區(qū)域;讀線程讀取整個(gè)緩沖區(qū)實(shí)時(shí)狀態(tài)并發(fā)送到對(duì)應(yīng)參數(shù)者的中間件。緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu)也是可以定制的,一種是采用先進(jìn)先出的鏈表結(jié)構(gòu)以保留歷史數(shù)據(jù);另一種緩沖區(qū)空間固定,只保留實(shí)時(shí)最新狀態(tài)。
該框架可柔性適應(yīng)多種場(chǎng)景,原因就在于分發(fā)服務(wù)器和中間件是可配置的。對(duì)于分發(fā)服務(wù)器,需要配置的必要信息有場(chǎng)景中的參與者數(shù)目、每個(gè)參與者的IO模式、每個(gè)參與者的網(wǎng)絡(luò)地址、每個(gè)參與者聯(lián)接的網(wǎng)絡(luò)協(xié)議和單次傳輸數(shù)據(jù)大小;對(duì)于中間件,需要配置感興趣的參與者的順序號(hào)及其角色、分發(fā)服務(wù)器網(wǎng)絡(luò)地址、單次傳輸數(shù)據(jù)大小。從配置信息可以看出,分發(fā)服務(wù)器不關(guān)心參與者的角色信息,中間件為解析數(shù)據(jù)必須要知道對(duì)應(yīng)參與者的角色信息。
該框架下,參與者之間不直接互聯(lián),而是以分發(fā)服務(wù)器為中心進(jìn)行數(shù)據(jù)交互。因此可實(shí)現(xiàn)增量開發(fā),即在場(chǎng)景中增刪參與者時(shí)只需要完善配置信息并對(duì)中間件進(jìn)行簡(jiǎn)單的二次定制即可實(shí)現(xiàn)。
圖4 基于“集中-分發(fā)”模式的數(shù)據(jù)集成架構(gòu)
該框架包括兩個(gè)軟件模塊,分別是分發(fā)服務(wù)器和中間件。按照本框架,這兩個(gè)軟件模塊具有多種實(shí)現(xiàn)方式,本文利用C++語言進(jìn)行了實(shí)現(xiàn),下面分別予以介紹。
按照框架要求,分發(fā)服務(wù)器除了緩沖區(qū)、分發(fā)模塊以外還需要有配置功能和網(wǎng)路傳輸功能,因此分發(fā)服務(wù)器還包括配置子模塊和網(wǎng)絡(luò)傳輸子模塊,模塊劃分如圖5所示。配置子模塊主要用來讀取配置文件,在正式運(yùn)行之前為分發(fā)子模塊和緩沖區(qū)子模塊配置好相關(guān)參數(shù)。網(wǎng)絡(luò)運(yùn)輸子模塊封裝了WinSock的一些常用函數(shù),以動(dòng)態(tài)鏈接庫(kù)的形式給出。緩沖區(qū)子模塊給出了存儲(chǔ)緩存各參與者傳輸數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。分發(fā)模塊處于分發(fā)器的核心,由配置子模塊配置其相關(guān)參數(shù),并調(diào)用網(wǎng)絡(luò)傳輸子模塊傳輸數(shù)據(jù)并對(duì)緩沖區(qū)子模塊進(jìn)行更新,完成節(jié)點(diǎn)間的數(shù)據(jù)分發(fā)。
圖5 分發(fā)服務(wù)器的模塊劃分
3.1.1 配置子模塊實(shí)現(xiàn)
分發(fā)服務(wù)器采用ini文件方式配置,程序中采用類的方式進(jìn)行封裝。類中的成員變量是配置參數(shù),由于程序中需且僅需一個(gè)參數(shù),因此都是靜態(tài)變量。成員函數(shù)_initialConfigParams是讀取相應(yīng)的配置文件為配置參數(shù)賦值。
3.1.2 網(wǎng)絡(luò)傳輸子模塊實(shí)現(xiàn)
為了重用代碼,將winsock的一些常用函數(shù)封裝為類,類繼承關(guān)系如圖6所示。
圖6 網(wǎng)絡(luò)傳輸子模塊類圖
其中,socketBaseClass作為基類,主要封裝了本地和遠(yuǎn)程地址以及Init、send和Receive三個(gè)成員函數(shù),在基類中Init、Send和Receive是虛函數(shù),需要在子類中進(jìn)行重寫。socketUDPClass對(duì)UDP協(xié)議相關(guān)的函數(shù)進(jìn)行了封裝,基類繼承了相關(guān)變量并對(duì)Init、Send和Receive進(jìn)行了重寫。由于UDP是不分服務(wù)端和客戶端的,所以只要一個(gè)類就可以實(shí)現(xiàn)。socketTCPClientClass和socketTCPServerClass封裝了TCP協(xié)議的相關(guān)函數(shù)。對(duì)于TCP而言,服務(wù)器端和客戶端的初始化過程是不同的,因此封裝為兩個(gè)類。
3.1.3 緩沖區(qū)子模塊實(shí)現(xiàn)
按照緩沖發(fā)送模式的要求,在分發(fā)器中設(shè)置一個(gè)緩沖區(qū)用來暫存各參與者的狀態(tài)數(shù)據(jù)。如圖7所示,當(dāng)要聯(lián)入m個(gè)參與者時(shí),緩沖區(qū)就平均分為m塊,每一塊用來緩存對(duì)應(yīng)參與者各狀態(tài)的數(shù)據(jù)。
圖7 分發(fā)器緩沖區(qū)數(shù)據(jù)結(jié)構(gòu)
實(shí)現(xiàn)時(shí)整個(gè)緩沖區(qū)作為一個(gè)數(shù)組,數(shù)組的成員又是一個(gè)先進(jìn)先出的隊(duì)列結(jié)構(gòu),而隊(duì)列中的每個(gè)成員又是指向?qū)嶋H狀態(tài)數(shù)據(jù)內(nèi)存的指針。因此,利用標(biāo)準(zhǔn)C++的STL庫(kù),將程序中的緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu)聲明如下:
由于目前聯(lián)網(wǎng)的參與者的數(shù)目是事先確定的,因此緩沖區(qū)數(shù)組的大小是一定的,也就是說對(duì)應(yīng)參與者的隊(duì)列是在聯(lián)網(wǎng)運(yùn)行前已經(jīng)確定好了。在聯(lián)網(wǎng)分發(fā)過程中變化的只有隊(duì)列中的元素,也就是要對(duì)隊(duì)列進(jìn)行壓入數(shù)據(jù)與彈出數(shù)據(jù)操作。對(duì)于壓入數(shù)據(jù)是比較簡(jiǎn)單的,當(dāng)某一個(gè)參與者狀態(tài)數(shù)據(jù)傳入時(shí)就壓入緩沖區(qū)對(duì)應(yīng)隊(duì)列的頭部;而彈出數(shù)據(jù)相對(duì)要復(fù)雜一些,原因就在于該數(shù)據(jù)要傳到除自身以外的所有參與者處后才能彈出。
為了實(shí)現(xiàn)這個(gè)邏輯,為隊(duì)列的每一個(gè)成員即參與者的狀態(tài)數(shù)據(jù)結(jié)構(gòu)加了一個(gè)頭部,該頭部作為標(biāo)志位數(shù)組是由一定數(shù)目的整數(shù)類型組成的,該數(shù)組的大小與參與者數(shù)目相同,數(shù)組中的數(shù)值取1時(shí)表示該狀態(tài)數(shù)據(jù)已發(fā)送到所在位置的參與者處,否則表示還沒有發(fā)送,當(dāng)標(biāo)志位全部為1時(shí)表示數(shù)據(jù)全部發(fā)送到位,程序會(huì)自動(dòng)刪除該狀態(tài)數(shù)據(jù),即完成了隊(duì)列狀態(tài)數(shù)據(jù)的彈出操作。
3.1.4 分發(fā)子模塊實(shí)現(xiàn)
如圖5可知,分發(fā)子模塊是分發(fā)器的核心模塊,其具體負(fù)責(zé)將接收到的參與者狀態(tài)數(shù)據(jù)分發(fā)到其他參與者處。分發(fā)子模塊是由上游子模塊和下游子模塊兩部分組成的,上游子模塊負(fù)責(zé)接收某一個(gè)參與者的狀態(tài)數(shù)據(jù)并將其寫入緩沖區(qū)內(nèi),而下游子模塊負(fù)責(zé)讀取其他參與者的狀態(tài)數(shù)據(jù)并發(fā)送回參與者處,如圖8所示。
圖8 分發(fā)子模塊組成
由圖8所示,根據(jù)“生產(chǎn)者-消費(fèi)者模式”,在上游子模塊和上游子模塊中都存在一個(gè)數(shù)據(jù)緩沖區(qū),該緩沖區(qū)主要目的也是為了分離收線程與寫線程或者讀線程與發(fā)線程之間的耦合關(guān)系,使其不相互依賴從而達(dá)到提高效率的目的。
分發(fā)子模塊中的收線程、寫線程、發(fā)線程和讀線程都是獨(dú)立運(yùn)行的線程,并且可知,在整個(gè)訓(xùn)練過程當(dāng)中,線程數(shù)目是一定的(與場(chǎng)景中的參與者數(shù)目成正比),并且各個(gè)線程一旦開始運(yùn)行就不會(huì)停下來,直到程序終止,也就是不會(huì)頻繁地進(jìn)行創(chuàng)建和消除,從而節(jié)省了調(diào)度時(shí)間,提高了分發(fā)效率。在程序開發(fā)過程中,涉及的開發(fā)難度主要體現(xiàn)在線程之間的同步,在程序?qū)崿F(xiàn)過程當(dāng)中主要采用關(guān)鍵代碼段的方式來實(shí)現(xiàn)的。
在4個(gè)線程當(dāng)中,收線程、寫線程和發(fā)線程的邏輯相對(duì)比較簡(jiǎn)單,重點(diǎn)描述一下讀線程。讀線程需要額外完成上文提到的數(shù)據(jù)彈出邏輯。讀線程主體是一個(gè)while循環(huán),首先申請(qǐng)存放讀取數(shù)據(jù)的內(nèi)存區(qū)并將其初始化為0,接著依次讀取緩沖區(qū)成員,當(dāng)某個(gè)緩沖區(qū)成員隊(duì)列不為空是從隊(duì)列尾部開始向前遍歷讀取狀態(tài)數(shù)據(jù),讀取時(shí)首先判讀對(duì)應(yīng)標(biāo)志位是否以置為1,如果是的話就繼續(xù)向前遍歷直到遇到標(biāo)志位為0的隊(duì)列成員,將該隊(duì)列數(shù)據(jù)存到內(nèi)存區(qū)域且設(shè)置標(biāo)志位為1,最后判斷該狀態(tài)的標(biāo)志位是否都為1,如果是則將該狀態(tài)數(shù)據(jù)彈出。其總體流程如圖9所示。
圖9 讀線程邏輯流程圖
由圖4體系結(jié)構(gòu)可知,中間層主要實(shí)現(xiàn)參與者與分發(fā)服務(wù)器之間的數(shù)據(jù)傳輸、打包與解析。對(duì)于不同角色的參與者而言,由于整個(gè)場(chǎng)景中的單次傳輸數(shù)據(jù)大小是相同的,因此傳輸模塊和打包模塊是固定不變的。而參與者角色有所不同,因此解析模塊是可定制的,從而實(shí)現(xiàn)將傳輸數(shù)據(jù)轉(zhuǎn)化為業(yè)務(wù)數(shù)據(jù)。
3.2.1 傳輸模塊的實(shí)現(xiàn)
傳輸模塊的組成如圖10所示,可以看到傳輸模塊的組成與分發(fā)服務(wù)器器的分發(fā)子模塊的組成是類似的,不同之處在于寫線程變成了寫函數(shù),而讀線程變成了讀函數(shù)。參與者在集成中間層時(shí),只需要周期性調(diào)用寫函數(shù)和讀函數(shù),就可以完成數(shù)據(jù)的接收與發(fā)送。
圖10 傳輸模塊組成
傳輸模塊中的發(fā)送數(shù)據(jù)區(qū)和接收數(shù)據(jù)區(qū)也是以隊(duì)列的數(shù)據(jù)結(jié)構(gòu),寫函數(shù)、發(fā)線程、讀函授和收線程之間的同步依然是通過關(guān)鍵代碼段。
3.2.2 解析模塊實(shí)現(xiàn)
在分發(fā)模塊和傳輸模塊中沒有涉及到各參與者的業(yè)務(wù)數(shù)據(jù),這樣就非常有利于擴(kuò)展參與者的角色。解析模塊就負(fù)責(zé)將傳輸數(shù)據(jù)轉(zhuǎn)換為程序能夠處理的業(yè)務(wù)數(shù)據(jù)。
我們知道,在內(nèi)存塊中數(shù)據(jù)都是以0、1的形式存在的,在沒有上下文的情況下是沒有任何意義的,但當(dāng)我們?nèi)〉媚骋粔K內(nèi)存的地址并將其解釋為事先定義好的數(shù)據(jù)結(jié)構(gòu),那么這塊內(nèi)存的數(shù)據(jù)就能得到解析了,如圖11所示。
圖11 利用定義好的結(jié)構(gòu)體解析內(nèi)存塊
傳輸模塊類中定義的接收數(shù)據(jù)區(qū)是一大塊數(shù)據(jù),它包含了所有遠(yuǎn)程參與者的狀態(tài)數(shù)據(jù),將參與者ID號(hào)與接收數(shù)據(jù)區(qū)的內(nèi)存塊建立一一對(duì)應(yīng)的關(guān)系,通過參與者的ID號(hào)就能找到對(duì)應(yīng)的數(shù)據(jù)塊。如圖12所示,由于單次傳輸數(shù)據(jù)大小是一定的,因此在分發(fā)器緩沖區(qū)被均分為N塊(N是參與者數(shù)目),每一塊的大小都是單次傳輸數(shù)據(jù)大小。那么分發(fā)器內(nèi)緩沖區(qū)的數(shù)據(jù)排列都是按照配置文件中參與者ID數(shù)組給出的順序排列,那么數(shù)據(jù)由分發(fā)器發(fā)往某個(gè)參與者時(shí)前后順序仍然是不變的(注意此時(shí)本地參與者數(shù)據(jù)不會(huì)發(fā)到本地參與者),那么再通過本地參與者定義的配置文件中的遠(yuǎn)程參與者數(shù)組所規(guī)定的順序就可以找到對(duì)應(yīng)ID遠(yuǎn)程參與者數(shù)據(jù)地址了。
圖12 參與者ID號(hào)與內(nèi)存塊中數(shù)據(jù)一一映射關(guān)系
為便于實(shí)現(xiàn)解析過程,提供了一個(gè)遠(yuǎn)程參與者類,如圖13所示。類中給出了5種默認(rèn)的參與者角色,分別是模擬器角色、教控臺(tái)角色、引導(dǎo)臺(tái)角色、VRForce產(chǎn)生的虛擬兵力角色和MaxSim產(chǎn)生的虛擬兵力角色。如果在場(chǎng)景中要加入新的角色,只要繼承該類并在字段中添加要擴(kuò)展的參與者的業(yè)務(wù)數(shù)據(jù)結(jié)構(gòu)并重寫initFromIni()函數(shù)即可。
圖13 遠(yuǎn)程參與者類結(jié)構(gòu)
某型飛機(jī)分隊(duì)?wèi)?zhàn)術(shù)模擬訓(xùn)練系統(tǒng)是面向單機(jī)、雙機(jī)、四機(jī)及以上相同或不同機(jī)型開展戰(zhàn)術(shù)協(xié)同訓(xùn)練科目而開發(fā)的。該系統(tǒng)由多個(gè)模擬器、教員臺(tái)、引導(dǎo)臺(tái)、虛擬兵力、態(tài)勢(shì)監(jiān)控等多個(gè)異構(gòu)子系統(tǒng)組成,如何將這些異構(gòu)的子系統(tǒng)進(jìn)行有效的數(shù)據(jù)集成并滿足系統(tǒng)間數(shù)據(jù)交互的實(shí)時(shí)性和可靠性是開發(fā)分隊(duì)?wèi)?zhàn)術(shù)模擬訓(xùn)練系統(tǒng)的一個(gè)重點(diǎn)和難點(diǎn)。采用本文提供的分發(fā)服務(wù)器和中間層軟件進(jìn)行了集成,集成后網(wǎng)絡(luò)結(jié)構(gòu)如圖14所示。
圖14 某型飛機(jī)分隊(duì)?wèi)?zhàn)術(shù)模擬訓(xùn)練系統(tǒng)網(wǎng)絡(luò)結(jié)構(gòu)
從該網(wǎng)絡(luò)結(jié)構(gòu)可以看出,該系統(tǒng)參與者數(shù)目N=12,參與者的角色有5種:模擬器(編號(hào)1到6)、引導(dǎo)臺(tái)(編號(hào)7、8)、VRForce計(jì)算機(jī)生成兵力平臺(tái)(編號(hào)9)、MaxSim計(jì)算機(jī)生成兵力平臺(tái)(編號(hào)10)、教員臺(tái)(編號(hào)11)和態(tài)勢(shì)監(jiān)控(編號(hào)12)。為每種角色的業(yè)務(wù)數(shù)據(jù)定義特定的數(shù)據(jù)結(jié)構(gòu)。
按照本文給出的方法集成的某型飛機(jī)分隊(duì)?wèi)?zhàn)術(shù)模擬訓(xùn)練系統(tǒng)已經(jīng)在部隊(duì)得到了初步的應(yīng)用,實(shí)踐表明,該方法能夠?qū)悩?gòu)的子系統(tǒng)快速進(jìn)行集成并滿足通信的實(shí)時(shí)性和可靠性要求。
本文旨在為連接節(jié)點(diǎn)數(shù)目不多、數(shù)據(jù)吞吐量不大,但數(shù)據(jù)交互實(shí)時(shí)性要求高的輕量級(jí)的分布式應(yīng)用場(chǎng)景提供一種易使用的系統(tǒng)集成方法、框架和實(shí)現(xiàn)。從設(shè)計(jì)和使用看,本文給出的框架具有模塊化、可擴(kuò)展性和可配置性的優(yōu)點(diǎn)。并且所有代碼都是基于C++標(biāo)準(zhǔn)庫(kù)完成,因此具有跨平臺(tái)的特點(diǎn)。下一步可進(jìn)一步提高分發(fā)服務(wù)器的柔性,達(dá)到根據(jù)參與者的請(qǐng)求進(jìn)行自動(dòng)配置的功能。