萬嘉龍,況立群,熊風光,薛紅新,韓 燮
(中北大學(xué) 大數(shù)據(jù)學(xué)院,山西 太原 030051)
隨著業(yè)務(wù)不斷變更和功能持續(xù)迭代,人們對軟件更新方式的要求越來越高。個人的開發(fā)經(jīng)驗、團體的技術(shù)積累以及需求的不斷變化,都會增加軟件部署的復(fù)雜度和風險。在實際開發(fā)中,項目集成以及組件的更新會消耗大量的時間,并且存在眾多潛在風險,往往等到項目開發(fā)結(jié)束的時候,才可能發(fā)現(xiàn)這些問題,最終可能導(dǎo)致項目延期。持續(xù)集成與部署可以使項目實施過程透明化,進而完善開發(fā)結(jié)構(gòu),改進開發(fā)質(zhì)量,降低交付風險[1]。隨著國家數(shù)字化進程的推進,越來越多的項目采用這種開發(fā)方式,持續(xù)集成在應(yīng)用系統(tǒng)中發(fā)揮著越來越重要的作用[2]。
傳統(tǒng)上通常使用人工干預(yù)的持續(xù)化部署模式,通過各種交互式手段完成自動化流程,以此實現(xiàn)對應(yīng)的代碼部署以及功能上線。Jenkins是一款持續(xù)集成和持續(xù)交付的開源軟件,常用于項目部署,不僅提供了直觀友好的用戶界面,而且可減少由于人工直接部署導(dǎo)致項目出現(xiàn)的系統(tǒng)兼容和版本沖突等問題[3]。但是這種方式不適用于部署頻繁改動的熱點代碼,因為這將導(dǎo)致頻繁的對整個應(yīng)用程序進行重新打包以及手工部署,由此產(chǎn)生較高的學(xué)習(xí)成本,且效率相對較低。
目前主流的部署方式大多采用Docker等虛擬容器,其本質(zhì)上是一個內(nèi)部封裝Jenkins或與Jenkins同類插件的系統(tǒng)[4-5]。虛擬化的持續(xù)化部署方式可以減少系統(tǒng)開銷,同時增加容器化的可移植性[6-8]。如果直接將應(yīng)用部署到系統(tǒng)中,一旦系統(tǒng)環(huán)境發(fā)生變化,會引發(fā)應(yīng)用程序的運行風險。虛擬容器的部署方式降低了部署難度和部署風險,并且在代碼頻繁改動的環(huán)境下,可以提高部署效率。但是,該方式仍然需要打包部署全部應(yīng)用程序,無法僅針對局部熱點代碼進行部分編譯部署,因此沒有從根本上解決熱點代碼部署效率低下的問題。
針對熱點代碼部署效率低下的問題,該文借鑒低代碼設(shè)計思想,提出一種面向Spring的熱點代碼在線部署方式,為專業(yè)計算機從業(yè)人員提供新穎的軟件開發(fā)部署模式,提升代碼質(zhì)量及部署效率[9-10]。該方法根據(jù)Bean容器找到對應(yīng)的算法代碼段進行數(shù)據(jù)抽取,利用在線部署的方式對代碼進行編譯、注冊及替換等操作,實現(xiàn)局部業(yè)務(wù)功能的在線更新。該方法應(yīng)用于新型智慧城市評估系統(tǒng),通過測試熱點代碼的頻繁更新迭代,驗證了在線部署模式的高效性。
Java的編譯分兩個部分,首先將源文件編譯成字節(jié)碼文件,然后字節(jié)碼文件被虛擬機加載以后變成機器碼[11]。對于開發(fā)者來講,主要的關(guān)注點在第一部分。在Spring的在線編譯中,應(yīng)用內(nèi)設(shè)置錨點,應(yīng)用外編譯代碼,再將指定的功能加入到應(yīng)用程序,這種在線編譯的加載機制可將代碼信息按照自定義格式加載到對應(yīng)的環(huán)境中。而容器化的機制是將編譯好的對應(yīng)代碼加入到Spring容器管理目錄,其關(guān)鍵思想是將新增的功能代碼直接加載到應(yīng)用,無需拉取所有代碼進行編譯發(fā)布[12]。
在線編譯的容器化加載機制如圖 1所示。前臺將代碼與類名稱傳入服務(wù)端,構(gòu)成輸入?yún)?shù),將其連同程序構(gòu)建的編譯參數(shù)、輸出參數(shù)以及程序錯誤輸出參數(shù)一起傳入到JavaCompiler編譯工具類。待JavaCompiler執(zhí)行成功輸出對應(yīng)類的class文件,如果執(zhí)行失敗將在程序錯誤輸出中寫入失敗信息。容器化過程將類名稱轉(zhuǎn)化成相應(yīng)的Bean名稱并構(gòu)成Bean定義,連同class文件一起傳入Bean工廠,由Bean工廠生成相應(yīng)的Bean容器并添加至Spring容器列表。
圖1 在線編譯的容器化加載機制
在線部署模塊是由容器配置模塊、熱點代碼編譯模塊和容器裝載模塊組成。用戶按照定義好的格式在容器化管理列表的代碼編輯功能頁面下進行代碼編輯,并通過代碼檢查。前端將代碼信息傳輸至在線部署模塊的服務(wù)端,服務(wù)端對獲取的信息進行解析,確定符合容器定義規(guī)范后,再對代碼進行編譯并生成編譯文件。在線部署模塊將對應(yīng)的文件傳輸?shù)较到y(tǒng)指定的目錄下,通過Spring將文件注冊到Bean列表,并交由Spring進行管理,具體流程如圖 2所示。
當應(yīng)用系統(tǒng)新增功能時,應(yīng)用系統(tǒng)首先要調(diào)用API實例化容器,然后提取相應(yīng)的屬性信息,最后由在線部署模塊將應(yīng)用數(shù)據(jù)裝入執(zhí)行單元,完成功能的調(diào)用。系統(tǒng)維護的過程中,如果目標執(zhí)行功能有變動,需要對目標執(zhí)行功能進行卸載再由在線部署模塊完成功能的新增。而在執(zhí)行功能卸載時,應(yīng)用系統(tǒng)需要通過容器化管理列表查詢Bean容器的信息,在Spring中將該容器卸載,在線部署模塊通過查找元信息中存儲的類名來刪除對應(yīng)的編譯文件,同時刪除存放的Bean信息和類文件。
容器配置模塊是基于當前流行的React框架并使用NodeJs進行編譯的前端平臺。該平臺使用React語言主要基于兩方面的原因,一方面是React使用虛擬Dom對頁面進行渲染,這種方法比直接操作Dom樹速度更快,另一方面React可以對功能細粒度進行封裝,從而提升組件的復(fù)用,且手動優(yōu)化性能更加便利,所以大型項目往往都會選擇React作為基礎(chǔ)框架[13]。
圖2 熱點代碼在線部署方法
如圖3所示,容器配置模塊是由容器化管理列表與對外提供服務(wù)兩個部分組成,其功能是為系統(tǒng)提供應(yīng)用管理、代碼編輯以及對外服務(wù)的API。其中應(yīng)用管理的功能是為在線部署模塊展示注入的應(yīng)用功能狀態(tài),以及對應(yīng)用功能進行基本操作。而代碼編輯模塊為容器化管理列表提供代碼編輯和代碼檢查的功能,方便使用者能夠摒棄繁瑣的配置,專注于指定功能的代碼編輯。為了使系統(tǒng)更加方便地調(diào)用容器配置模塊所構(gòu)建的功能,在容器配置模塊中加入了對外服務(wù)的API,使應(yīng)用系統(tǒng)能夠依靠簡單的配置就可以完成對注入功能的測試,同時也能為容器化管理列表提供對應(yīng)的功能索引,方便進行應(yīng)用功能的基本操作。
圖3 容器配置模塊結(jié)構(gòu)
代碼編譯模塊使用了Java軟件開發(fā)包中自帶的java-tools動態(tài)編譯模塊,可以對字符串構(gòu)成的代碼進行編譯,并能提供豐富的編譯接口。該模塊將從前端傳輸來的代碼和元信息存儲到數(shù)據(jù)庫,并將數(shù)據(jù)庫中的對應(yīng)狀態(tài)標記為傳輸完成。隨后,代碼在該模塊進行編譯,如果編譯成功,模塊生成編譯文件,并將生成的文件放到指定位置,然后把編譯成功的狀態(tài)傳輸?shù)饺萜餮b載模塊,接著執(zhí)行后續(xù)操作。反之將編譯失敗的信息,連同對應(yīng)的元信息一起傳輸?shù)綌?shù)據(jù)庫中,并將數(shù)據(jù)庫中的對應(yīng)狀態(tài)標記為編譯失敗。
常規(guī)采用的重新編譯部署方式如圖4的上部分所示,重新編譯部署方式在功能修改時,由用戶開發(fā)好相應(yīng)的功能代碼,并將代碼上傳至代碼管理平臺。用戶要完成應(yīng)用的部署還需使用持續(xù)集成工具Jenkins,將代碼拉取至服務(wù)端,Jenkins在服務(wù)端對代碼進行編譯,然后編譯生成Jenkins構(gòu)件,啟動構(gòu)件即完成部署。該文采用基于在線部署的編譯方式(見圖4下半部分),用戶是在在線部署模塊中進行編碼,服務(wù)端僅負責接收和編譯由在線部署模塊傳輸過來的代碼,之后將生成的文件通過Spring的環(huán)境直接注入到系統(tǒng)中,進而完成項目的部署。與重新編譯部署方法相比,在線部署編譯方式減少了代碼拉取以及構(gòu)件啟動的時間。在應(yīng)用功能未確定時,功能的改變會引發(fā)頻繁部署,采用在線部署方法可以節(jié)省很多的時間。
圖4 重新編譯部署與在線部署的編譯方式對比
容器裝載流程如圖5所示。
圖5 容器裝載流程
容器裝載模塊將Spring應(yīng)用環(huán)境的上下文實例化,通過加載具有復(fù)合注解的代碼,將應(yīng)用功能注入到Spring環(huán)境。該模塊從編譯模塊獲取編譯成功的文件和元信息并加入到Bean處理單元,然后將新生成的Bean容器注冊到Spring環(huán)境。如果注冊成功,則依據(jù)元信息在數(shù)據(jù)庫中找到指定記錄,并將其狀態(tài)更新為部署成功,然后把對應(yīng)的狀態(tài)寫入到日志管理器,反之,注冊失敗則標記部署失敗。
容器注冊的核心是將熱點代碼轉(zhuǎn)化成Bean容器。對所需功能注冊時,將類名與Bean的ID進行綁定,保證在Bean注冊機制下,注冊的容器是唯一有效的。容器是整個Spring環(huán)境的重要組成部分,在應(yīng)用系統(tǒng)中,Bean的實例化獲取與銷毀都由Spring進行管理。如圖6所示,當在線部署某一應(yīng)用程序時,采用不停機的工作模式,事先定義好該程序的計算接口和數(shù)據(jù)信息接口,然后在代碼編輯模塊中實現(xiàn)上述兩個接口。其中,計算接口用于實現(xiàn)具體的應(yīng)用功能,而數(shù)據(jù)信息接口用于實現(xiàn)容器配置模塊對應(yīng)用功能的管理。接口代碼完成編譯后,Spring調(diào)用Bean管理器將轉(zhuǎn)化好的容器注冊到容器列表,為代碼無侵入做準備。應(yīng)用在停機部署時,卸載在線部署模塊不會影響系統(tǒng)的使用。依據(jù)不同開發(fā)階段的要求,在線部署模塊可以靈活制定相應(yīng)的部署策略,降低開發(fā)與運維的工作難度,保障系統(tǒng)的穩(wěn)定,降低項目管理成本。
圖6 運行與停機狀態(tài)下的容器注冊方法
實驗的硬件環(huán)境是Intel(R)Corn(TM)i7-7700HQ CPU @ 2.8 Hz 2.81 GHz,內(nèi)存為8 GB,軟件環(huán)境為windows tomcat idea2022。實驗在Spring官網(wǎng)下生成空白的項目,添加相應(yīng)的功能構(gòu)建不同復(fù)雜度的系統(tǒng),以便對比不同部署方式的部署效率。
為了評估應(yīng)用在不同軟件復(fù)雜度下的在線部署方法與重新編譯部署方法的效率,采用不同維度下的軟件復(fù)雜度進行實驗,衡量軟件復(fù)雜度的常用度量方法,包括代碼行數(shù)度量法和Halstead度量法。其具體構(gòu)建如下:
代碼行數(shù)度量法在空白項目中寫入100行代碼形成一個請求單元,標識系統(tǒng)擁有100的復(fù)雜度,將代碼請求單元擴增至10個請求單元,可以使代碼行數(shù)復(fù)雜度增加10倍,變?yōu)? 000的復(fù)雜度,以此類推,構(gòu)建10 000、100 000的代碼行數(shù)復(fù)雜度的測試項目。同理,使用Halstead度量法構(gòu)建不同復(fù)雜度的項目,向空白項目中寫入定義好的度量單位為100的模塊,以此為模板擴增,與代碼行數(shù)復(fù)雜度一致,分別增加至10 000和100 000的復(fù)雜度。度量單個單元復(fù)雜度的Halstead公式如下所示:
Mcount=0.05(N+N1)
其中,N為程序詞匯表的長度,N1為程序長度,為了與代碼行數(shù)度量法的對比更加直觀,設(shè)定了0.05的系數(shù)。
對重新編譯部署與在線部署兩種方法在時間效率上進行對比,從程序開始編譯計時,到實際應(yīng)用部署為止,得出各自所需的時間。實驗中選擇不同度量法下的不同系統(tǒng)復(fù)雜度,在進行10次相對獨立的實驗后取平均值。其中T1表示在線部署的時間,T2表示重新編譯部署的時間,T1/T2是兩種部署模式下的時間比值。
表1 兩種部署方式在代碼行數(shù)復(fù)雜度下的對比
表2 兩種部署方式在Halstead復(fù)雜度下的對比
如表1和圖7所示,隨著代碼行數(shù)復(fù)雜度的增加,在線部署的時間明顯小于重新編譯部署的時間,并隨著代碼行數(shù)復(fù)雜度的增加,時間比值在逐漸下降。如表2和圖8所示,Halstead復(fù)雜度和代碼行數(shù)復(fù)雜度的結(jié)果基本一致。結(jié)果表明,在線部署比重新編譯部署效果更好,推廣到現(xiàn)在大規(guī)模、復(fù)雜性高的系統(tǒng)中,運用該熱部署方法部署代碼更有利于節(jié)約時間和資源。
圖7 兩種部署方式在代碼行數(shù)復(fù)雜度下的對比
圖8 兩種部署方式在Halstead復(fù)雜度下的對比
該文提出的在線部署方法應(yīng)用于新型智慧城市評估系統(tǒng)的評估體系構(gòu)建模塊。隨著城市的不斷發(fā)展,城市評價的指標體系也在不斷地進步,指標計算也越來越復(fù)雜。在系統(tǒng)業(yè)務(wù)開發(fā)的過程中,對城市評估的評估體系往往考慮得不夠全面,所以在系統(tǒng)運行初期,會不斷地調(diào)整指標中的評估算法,與之相對應(yīng)的測試環(huán)境以及預(yù)生產(chǎn)環(huán)境的應(yīng)用也需要頻繁地部署。針對這種情況,系統(tǒng)為其設(shè)計了可在線編輯的指標計算模塊,該模塊通過在線操作完成新指標的部署。
當用戶對指標計算模塊編輯時,先在指標編輯的模塊中對指標計算的算法進行編輯,之后系統(tǒng)根據(jù)算法的名稱和配置構(gòu)建元信息,將代碼連同元信息一起傳輸?shù)较到y(tǒng)后臺,并在后臺對算法進行編譯,然后將編譯后的文件通過Spring注入到容器列表,即可完成對應(yīng)指標的部署。在對城市評估的時候,評估系統(tǒng)使用元信息查找到對應(yīng)的算法,完成指標的計算。該在線部署方法為在線評估系統(tǒng)的指標拓展計算提供了思路,同時很好地解決了頻繁部署熱點代碼的問題。
研究了基于Spring的在線熱點代碼部署方法,并在新型智慧城市評估系統(tǒng)進行了驗證。在系統(tǒng)部署的過程中,運維人員會將所有的代碼拉取到服務(wù)器中,對應(yīng)用重新部署。功能應(yīng)用在頻繁改動的時候,重復(fù)拉取代碼后進行部署,很容易導(dǎo)致一些問題出現(xiàn)。該方法能夠使應(yīng)用系統(tǒng)集成的在線部署模塊在不停機的狀態(tài)下,穩(wěn)定地增加功能,并以較快的速度部署,同時在應(yīng)用必須停機時,卸載在線部署模塊,零配置重新部署功能 。該文設(shè)計的在線部署方法有望在緊急上線、預(yù)生產(chǎn)環(huán)境調(diào)試等環(huán)境下發(fā)揮作用[14-15]。
同時,該部署方案還有進一步完善的空間。在應(yīng)用過程中熱點的代碼只能修改單獨的類,只能在線單獨改變其中一個功能,這種方法免去了代碼的整包構(gòu)建,提升了速率。但是這種方案僅僅只能在熱點代碼中使用,不適合大范圍的修改代碼,此外對于復(fù)雜環(huán)境的分布式系統(tǒng),還需要考慮各個層級的依賴問題。