軟體工程與大型整合專案—以WiMAX整合型計畫為例 (3/3)

Continuous Integration

在討論版本控管時,我們曾經提到WiMAX計畫使用一個輔助工具「JCIS」進行持續整合(Continuous Integration)的工作。那麼,為什麼要做持續整合呢?維基百科中對於持續整合的理論有些初步的介紹,包含下列十個重要的Practices:
  1. 維護一個程式庫(Maintain a code repository)
  2. 將建置自動化 (Automate the build)
  3. 使建置能進行自我測試(Make the build self-testing)
  4. 每人每天送交程式碼(Everyone commits every day)
  5. 每次送交都應該被建置(Every commit should be built)
  6. 維持快速的建置時間(Keep the build fast)
  7. 在實際運行的環境下進行測試(Test in a clone of the production environment)
  8. 簡化取得最新可交付版本的方法(Make it easy to get the latest deliverables)
  9. 每個人都能看到最新版建置的測試結果(Everyone can see the results of the latest build)
  10. 將佈署自動化 (Automate deployment)
上述許多Practices都要求自動化,自動化又可以分為半自動和全自動,以半自動為例,可能是有人每天固定手動執行make指令,將所有要建置的檔案編譯與連結,接著有人將結果複製到實際運行的環境,再下指令讓電腦執行所有的測試,這個過程雖然必須由人來操作,但只要下少許指令就可以完成了。那麼全自動呢?就是在專案開始的時候進行若干設定,之後由電腦自動處理一切編譯、連結、測試等工作,這就需要一套輔助工具了。

要確實完成這麼多的Practices,其實門檻是很高的。即使是在企業界,也難免會有不遵守遊戲規則的軟體工程師,更何況我們的開發主力是學校裡沒什麼專案成敗壓力的學生。舉例說,要做到「每人每天送交程式碼」幾乎是不可能的,基本上學生開發程式是有一天沒一天的,最多只能要求到「有修改就必須送交程式碼」。另外像是「每次送交都應該被建置」,在我們第一年的WiMAX計畫中可以說是完全失敗。在開發環境未能統一的情況下,子計畫的開發人員往往以為只要在自己的開發環境上能建置就沒問題了,於是就送交程式碼,結果,導致其他子計畫在建置的時候發生錯誤,或者是因為送交程式碼的時間差,導致程式碼整合時出現問題,這些都很常見,也非常痛苦。

實務上要完全避免錯誤是很難的,然而,換個角度想:「就讓事情發生,然後立即處理」,這也是近幾年軟體專案管理常見的作法,甚至建議能夠有一套機制,讓問題越早發現就更好了。簡單地講,與其讓問題累積了很長一段時間才一次爆發、難以收拾,還不如讓事情及早發生,提早面對,才是上策。因此,我們在WiMAX計畫的第二年起開始導入持續整合的機制,當然,使用持續整合系統並沒有辦法避免問題發生,但是,如果有任何計畫成員沒有送交程式碼,或是送交的程式碼有問題,又或是測試沒有通過,每天查看報表的專案經理就能發現問題,儘早處理。

圖1是JCIS運作的情形。JCIS執行的環境必須包含實際運行環境的機器,以WiMAX為例,主控台為Java程式,理當能在Windows和Linux都能正確執行,而模擬代理人就只能在Linux上執行。JCIS能同時支援Java與C/C++程式的建置,並且能執行建置後的自動測試,而且許多麻煩的設定,都可以透過JCIS自動完成,因此,搭配JCIS就能完成持續整合所需的大部分Practices。在WiMAX計畫中,我們有專人維護Subversion,所有(包含總計畫及子計畫)開發者的修改都送交到Subversion,而JCIS Server則固定每天到Subversion取出最新版本的程式碼,放到不同的環境下,進行建置、測試與佈署的動作,最後回收結果並整理成報表。為了讓JCIS能分析CppUnit執行的結果,我們還特地加入額外的程式碼,讓CppUnit執行的結果能以XML格式輸出,以便JCIS製作報表。



圖 1 完整的JCIS運作情形


在WiMAX計畫第二年持續整合的機制建立起來以後,總計畫專案經理每天都會看JCIS的報表;第三年曾經一度因為換JCIS主機,中斷了一段時間,恢復後,改用JCIS的RSS功能,由JCIS每天寄報表到專案經理的信箱,也仍然是每天都會看。聽起來不錯,那麼成效呢?我們的感覺是「有幫助,但並沒有很完美」,主要的問題是「Unit test太少」,限制了持續整合的效果,也就是說,報表雖然能呈現測試結果,但是,通過測試並不等於沒有問題。另外,許多子計畫的程式也沒有天天送交,因此,持續整合的功能實質上僅僅相當於Daily Build。

如果要充分發揮持續整合的效果,在每天閱讀JCIS報表後,應該多做一些額外的改善措施,例如增加Unit Test、要求開發人員送交程式等,但是,受限於學生的時間,這些措施往往沒有貫徹或生效,結果在進度監控上也沒有充分發揮效果。整體來說,我們的WiMAX計畫在前述的10個Practices中,第1、2、6、7、8和9項做得還不錯,而剩下的部分則還需要再加強,才能讓JCIS發揮應有的功能,而不僅是Daily Build的機器。結論是觀念與工具都沒有錯,但是我們做的不夠落實,因此,即便有再好的工具輔助,團隊成員都應該用心維持紀律,否則工具的效果不免大打折扣。

當然,有持續整合與版本控管還是有好處的。至少不論在哪個時間點,我們隨時都可以拿得出一個可以運行的版本,隨時都可以展出。

作者:陳偉凱、杜秉穎

Software Architecture Evaluation
討論完測試相關的做法後,我們回頭探討系統架構(Software Architecture),介紹我們評估與選擇系統架構的方法。其實系統架構應該是在開發初期就必須深入探討的,我們把系統架構放在後面說明是為了讓讀者能先熟悉我們的系統,以便進一步解釋我們的選擇。

系統架構應該如何設計呢?當然是依據「需求」而定,不過在開發過程中,需求並不是永遠不變的,當需求出現重大變動時,系統架構不免面臨衝擊,而已經設計好的程式也會受到重大的影響。以WiMAX計畫為例,第一年設定的目標很保守,這是因為我們沒有足夠的經驗,所以希望能「安全」地完成一個簡單的模擬環境,因此,第一年只考慮:「電腦A上的『H.264即時視訊傳輸』程式可以經由模擬的『WiMAX網路』將影像傳輸到電腦B上播放,而WiMAX網路僅支援『單向、點對點』的模擬」,這是一個關鍵的功能性需求,但是,有點過度簡化,並不是完整的需求。

我們的WiMAX模擬軟體在第一年結束時確實開發完成,並且在2007年的台北應用展(世貿)中展出。但是,國科會期末審查時,審查委員給予我們許多寶貴的建議,於是綜合第一年的經驗,我們重新調整需求,將第二年、第三年的目標擴充為:「電腦A上的『任何』程式可以經由模擬的『WiMAX網路』傳輸資料到電腦B、WiMAX網路必須支援『雙向、點對多點』的模擬,並且隨時能由『主控台』觀察WiMAX網路的模擬過程或現象」。嚴格講這些擴充的需求並不是一開始時我們完全沒想到,而是在「安全」的前提下,暫時被擱置了。

第二年計劃在第一年的基礎上開發時,我們很快就發現既有的系統架構無法滿足新的需求,需要有全新的系統架構,而且,修改系統架構的代價將是非常昂貴的。既然系統架構非改不可,我們有一個共識:「希望系統架構有足夠的前瞻性,使得第三年不需要再修改一次」。經過幾番討論後,出現二個主要候選架構:(1)分散式架構、(2)集中式架構。這兩種架構都可以搭配Linux虛擬網路驅動程式,取得網路應用程式的封包,讓我們能將封包作為「WiMAX模擬網路」的輸入,以具體模擬WiMAX網路的媒體層、實體層與通道現象。其中,分散式架構比較接近真實的網路拓樸,運算能力也比較容易擴充,而集中式架構則比較接近第一年的程式,更動的幅度比分散式架構小,且所需要的電腦數量少,佈署也較容易。

為了能更理性地評估二種候選架構,我們在第二年投入更多時間評估系統架構。這時候我們發現Light-Weighted CMMI 系統設計文件(SDD)所要求的內容其實是很實用的,我們參考了ATAM,根據WiMAX計畫的特性,在評估時納入下列Quality Attribute,其中有些是相輔相成的,但有些則彼此衝突的:
  • Monitor Responsiveness:我們參考許多網路模擬軟體,有的僅能在模擬完成後呈現模擬結果,有的則能在模擬過程中,同時呈現當時的結果,相較下後者能提供較佳的系統回應能力,因此我們希望能提供即時監控。
  • Emulation Performance:由於我們使用軟體模擬硬體的功能,因此,勢必需要龐大的運算能力才能維持一定的模擬速度,因此,系統架構應能夠視需要擴充運算能力。
  • Emulation Correctness:模擬軟體的擬真度非常重要,網路節點的數量、封包產生的頻率、節點之間的距離、協定層的實作等因素都會影響模擬結果,系統架構應該將上述因素都考慮進去。
  • Emulation Scalability:第一年系統架構無法同時支援多個基地台,以及多個行動用戶,我們希望新的架構能依使用者的需求擴充基地台及行動用戶的數量。
  • Testability:雖然第一年的系統最後還是整合完畢,但是卻發現系統相當難測試,因此我們將測試提高到系統架構層級,期望能提高程式的可測試性。
  • Module Replacement:依據第一年的開發經驗,各子計畫的開發進度不一,有的超前,有的落後,因此,在整合及測試時期,我們希望系統架構能夠提供繞過(Bypass)或是替換(Replace)部分模組的能力。
  • System Runtime Configurability:透過模擬系統,使用者可以觀察各協定層運作的情形,因此,系統架構最好能提供隨時修改組態(Configuration)設定的能力,以便觀察各種組態的運作情形。
  • System Deployment:候選的方案中,分散式架構會增加Emulation Scalability,但同時也會提高佈署(Deployment)的複雜度。
  • System Extensibility:WiMAX的規格一直在演進,第二年的目標是實作802.16d標準的模擬環境,而第三年則是802.16e標準,因此,系統架構必須考慮標準的轉換。
  • API Standard Compliance:有鑑於大多數的網路應用程式都是使用標準的Socket API,因此系統架構必須和Socket API相容,讓現有的網路應用程式不需修改及編譯就能在WiMAX模擬網路環境下執行。
  • Seamlessness Migration:能大量利用舊的程式碼是很重要的。雖然新的架構能帶來許多好處,但是,在人力資源有限的情況下,轉移成本也是很重要的考量。
找出Quality Attributes後,依專案的特性給予Quality Attribute各別的比重,作為評估的準則(Criteria)。從表 1可以得知,模擬的正確性及API標準的相容性是眾多評估準則中最重要(比重5)的二項,其次(比重4)是模擬的規模、模組的替換及系統的可擴充性,接著(比重3)是監控的即時性、模擬的效率、系統組態及架構轉移成本,最後(比重1)才考慮到系統的佈署。這些比重的分數是討論出來的,雖然還是有些主觀的成分,但至少是多數人的意見。

接著我們用Use Case主要的幾個Scenario,逐項分析當套用某個候選架構時所帶來的影響,例如變簡單或是變困難?並給予一個分數,當然這分數也是討論出來的,最後乘上比重後累加得到一個最高分的架構,這個架構也就是我們最後採用的系統架構:「分散式代理人搭配Linux虛擬網路驅動程式」。我們將在下週再進一部介紹該架構。

表1 ATAM分析結果


作者:陳偉凱、杜秉穎

Software Architecture

上次提到我們選擇的系統架構是分散式代理人(Distributed Agent) 架構。圖 1是系統架構圖,由圖中可以看出,各行動台(Mobile Station)、基地台(Base Station)、Gateway、主控台(Console)分別各自使用一部電腦負責模擬或執行,透過實體網路(圖中藍色部分)連結成一套完整的WiMAX模擬系統,因此模擬時,運算是分散到不同的電腦上執行的。另外,為提高模擬的便利性,除了主控台與Gateway以外,我們在每一部電腦上都部署一支代理人(Agent)程式(圖中綠色標示Agent的部份) 與WiMAX虛擬網路卡驅動程式,由主控台指揮代理人,依模擬的需求扮演行動台、基地台、Gateway等角色,以便進行模擬。


圖1 WiMAX Simulator System Architecture


但是,WiMAX通訊協定本身就是Layer架構,會不會與我們選擇的架構衝突呢?事實上是沒有,因為,我們是從不同的觀點看系統架構。舉例說,當某甲行動台的應用程式(例如FTP)與某乙行動台的應用程式(例如FTP server)互通時,某甲的應用程式會透過Linux的Socket API (圖中的Socket over virtual IP),再透過WiMAX Virtual Driver,將應用程式的封包(packet)送到代理人,此時代理人模擬WiMAX行動台,因此,代理人將封包依WiMAX的規格,經MAC、SECU、 PHY等通訊層處理,產生實體的WiMAX電波訊號,然後,訊號再經過通道效應(CHN)產生失真,傳至基地台,再轉傳送至某乙行動台。換句話說,WiMAX的各通訊層由代理人負責模擬,而多個代理人同時進行模擬時,則構成分散式的WiMAX模擬系統。

當使用者想要模擬WiMAX網路時,首先透過主控台的使用者介面(圖 2),設定所欲模擬的網路環境,包含地形(都市、市郊、山區等)、基地台的特性(Properties)、數量與距離、行動用戶端的特性、數量、移動速度及與基地台的距離等,然後啟動模擬。當啟動模擬時,主控台會先進行初始化的工作,依使用者指定的模擬環境,透過控制埠(Control Socket)送出命令,設定代理人應該扮演的角色(行動台、基地台或單層模擬),待初始化完成後,行動台的應用程式便可以透過Socket API,指定虛擬IP建立(虛擬)WiMAX網路連線,而其封包就會被Linux作業系統送入虛擬網路卡驅動程式,然後導入代理人程式做進一步的處理,再藉由資料埠(Data Socket)將模擬出來的WiMAX訊號(資料)傳送給其他代理人。


圖2 模擬環境設定畫面

分散式代理人的架構將整個模擬系統拆解成幾個子系統。而在個別子系統中,為了解決其他問題,我們又搭配使用其他的系統架構。例如,為了讓WiMAX各層通訊模組能於執行期間動態地被載入、組合與卸除,我們在代理人程式內部使用「Pipe & Filter」架構,每一層網路模組均被視為一個Filter,透過Filter一致的標準化介面,代理人程式在收到主控台的命令後,就可以用組合積木的方式,選擇若干個Filter,形成一個處理網路封包的Pipe,而封包就像水一樣,流經Pipe中的Filter,由各個Filter對封包進行一連串的模擬工作。

在模擬的過程中,代理人每一層的WiMAX通訊模組(MAC、SECU、PHY、CHN)都會產生資料流與控制流,其中資料流由資料埠傳送到另一個代理人,而控制流則傳送到主控台。控制流的資料是為了讓使用者可以監控模擬過程而特別收集的,這些原始資料(Raw Data)有各種不同的呈現方式,為了讓主控台能彈性呈現各種監控資料,我們使用了Plugable的架構,監控的畫面由SPMT外掛(Plug-in)負責,如圖 3。當主控台啟動時,會檢查系統路徑下可載入的外掛,並自動載入外掛,即使沒有SPMT外掛時,主控台還是能運行並控制代理人,若想增加新的資料呈現方式,只需提供新的外掛並加到系統路徑下即可。外掛與外掛之間則是透過服務(Service)溝通,所有外掛在被載入時,會登錄外掛提供的所有服務,任何外掛皆可以用「名稱」向主控台請求服務,例如:想取得監控資料的外掛,可向主控台請求「Register Monitor Data Listener」服務,該服務由核心外掛所提供,會將外掛註冊到清單中,當有新的監控資料抵達時,便會通知所有註冊的外掛。



圖3 SPMT Plug-in提供監控圖表


除了上述的「分散式代理人」、「Pipe & Filter」及「Plugable」等架構外,我們也要求所有主控台的外掛採用MVC (Model-View-Controller)架構,這些架構確實讓系統的模組與分工更加明確。但是,從系統架構到寫成真正的程式之間,還有一些落差需要彌補,就像是一個建築的鋼骨結構完成後,還需要水電配線與室內裝潢,讓建築可以使用,在軟體中,這便是設計樣式(Design Pattern)派上用場的時候了,我們將在下次介紹數個我們使用的設計樣式。

作者:陳偉凱、杜秉穎

Design Pattern

有了軟體架構,開始進入細部設計。我們使用若干個設計樣式(design pattern)提升程式的cohesion、降低coupling,使得程式更容易維護及修改。以下簡單介紹幾個(不是全部)我們用到的設計樣式及設計方式。

  • State Pattern:WiMAX模擬代理人會依據主控台的命令切換狀態,扮演不同的角色(如BS、MS、…等)。就程式設計的觀點,每個不同的狀態(角色),所需載入的通訊模組和執行流程都不相同,如果各種狀態的程式都集中寫在同一個class,不免會出現許多狀態切換的邏輯,使得程式難以擴充及修改。因此,我們使用圖 1的State pattern來管理狀態,而主程式核心SYSAgent,只需將處理通訊協定的部分委派(delegate)給目前使用中狀態物件處理即可(StateHandler)。


    圖1 State Pattern

  • Adapter Pattern:對前述StateHandler而言,不同的Concrete StateHandler會載入不同的通訊協定層,而且各通訊層所需的資料、控制方式都不同,介面也不相同,甚至數量多寡也不一致。為了簡化通訊層的操作方式,以便彈性地抽換通訊模組,我們將通訊層的介面統一為傳送(Send)與接收(Receive),如圖2,並使用Adapter pattern橋接StateHandler,再由Concrete Adapter轉換為實際模組所提供的介面。此外,我們使用上回提到的Pipe & Filter架構,在Adapter中有一個指標,指向下一個Adapter,可以將Adapter依特定的順序串接,串接後的組合便形成一個Protocol stack。


    圖 2 Adapter Pattern

  • Factory Pattern:雖然使用State pattern與Adapter pattern可以簡化狀態與介面的管理,但是,卻也衍生出其它的問題:「如何產生StateHandler與Adapter的物件?」。如果讓SYSAgent直接產生(new) Concrete StateHandler物件,會讓SYSAgent依賴(depend) Concrete StateHandler,而不是StateHandler介面,而且物件產生的程式也會散落在各處而難以管理,因此我們使用Factory樣式(註:不是GoF裡的Factory method pattern或Abstract factory pattern),令SYSAgent透過StateHandlerFactory產生Concrete StateHandler時。同理,Concrete Adapter物件的產生也用同樣的方式處理。


    圖 3 Factory Pattern

  • Command Pattern:主控台的圖形使用者介面使用Java Swing開發,Java Swing建議使用Action來包裝按鈕、功能表或工具列所呼叫的函式(圖 5),Action可以應用Command pattern將函式物件化,如此,GUI元件和程式邏輯切割,不會因為不同程式有改變的需要,只需實作Action介面讓GUI元件呼叫。


    圖 4 Java Swing Action


    前面提到我們使用State pattern管理狀態,但是,如果直接讓StateHandler接收及處理主控台的命令,會讓StateHandler與SYSAgent間的耦合太強烈,因此,如圖 5所示,我們使用一個RemoteCommandListener專職從網路(透過Socket)接收命令,然後將命令重建為RemoteCommand,塞入一個Command佇列中,StateHandler只需檢查佇列是否有新的遠端命令,如果有便從佇列中取出後直接呼叫execute()函式,StateHandler不需知道遠端送來的命令為何,也不需要知道要呼叫SYSAgent的哪個函式,一切都交給對應的RemoteCommand處理。


    圖 5 Agent-side Command pattern

    主控台同樣使用Command pattern,圖 6中NetworkStation是佈署的最小單位,一個NetworkStation對應一個WiMAX模擬代理人,當網路初始化完成後,與WiMAX模擬代理人的溝通都是利用TriggerCommand及ResponseCommand。TriggerCommand為送給WiMAX模擬代理人的命令,ResponseCommand為WiMAX模擬代理人送回的回應。



    圖 6 Console-side Command pattern


配合Design Pattern的使用,我們將設計類別圖(Design Class Diagram)畫出來,作為程式設計的藍本,然後開始開發程式。有了設計類別圖,讓我們的開發團隊能更有效地分工與合作,更重要的是Design Pattern讓每個類別都有清楚的責任,在維護上也相對變得比較容易。WiMAX總計劃(不含子計畫)第三年的程式碼規模(含測試碼)C/C++部分大約有27,000餘行,Java部分則有8000餘行,每當發現一個Bug或是需求修改時,都能快速定位,僅需要修改少量的類別即可,這都是善用Design Pattern所帶來的好處。

作者:陳偉凱、杜秉穎

結語

新年期間,首先祝大家新年快樂!虎年行大運!

最近黃教授所提供的軟體工程諺語提到:「製作電腦程式仍然是人類所曾承擔過最困難的工作之一;要精通程式製作需要才能(諸如分析、溝通...等能力),創造力,智慧,邏輯,創建與使用抽象,以及經驗─即使有最好的工具」。是的!過去三年,因為WiMAX整合型計畫,讓我們在教學之外,能親自動手開發一個大型軟體,也因此更深深地體會到:「如這個諺語所云,開發大型軟體真的很難!」。表 1是我們(總計劃與各子計畫)開發的模擬軟體程式碼統計(不含WiMAX應用與其他軟體輔助工具),總共有超過18萬行原始程式整合在一起,三年累積投入人力近146人(表 2),很慶幸地,以成果來說,計畫是成功的。


表1 三年程式碼(LOC)統計



表2 三年投入人力統計



面對這個「大長多多」(龐「大」、執行時程「長」、參與團隊「多」、投入很「多」人力開發) 的專案,軟體工程能力確實是成敗的關鍵因素。雖然我們沒有完全實踐各種軟體工程的準則(Principle),但軟體工程確實幫助我們達成目標。在整個執行過程中,軟體流程中的分析(Analysis)、評估(Evaluation)、架構(Architecture)與設計(Design)都扮演了相當重要的角色。此外,良好的溝通(Communication)也是很重要的,我們運用文件與UML將溝通後的共識變成開發的基礎,Use Case被應用在需求分析上,使得溝通不是雞同鴨講,另外,我們也利用循序圖(Sequence Diagram)作為串接各個子計畫間溝通的橋樑,彌補不同領域的隔閡,使得而UML不僅是設計圖,而是有效的溝通工具。

或許軟體設計是一門「藝術」。但是,我們還是可以利用一些科學的方法,例如ATAM,從眾多的經驗中,找到適合自己的設計。這些眾多的經驗有大有小,透過許多智者的整理,變成了Architectural Pattern及Design Pattern,這些Pattern讓我們不用重新打造輪子,試想當自己打造的輪子都不見得比較好用時,我們為何不用既有的輪子、引擎和許許多多零件,組合出一台車子呢?只要有好的組合與適當的調整,我們可以更穩健地建造跑得又快又穩的車子,何樂而不為呢?

我們對於實作(Implementation)的方法著墨不多。但是實作時,在多人共同開發的專案中,開發環境、版本管理和紀律都是非常重要的,如果沒有這些基礎,程式碼就像是一盤散沙,無法堆疊出可用的軟體。一致性的開發環境,乃至統一的環境設定,不但可以避免不必要的相容性問題,也減輕開發者在設定環境時的負擔;版本管理系統能讓開發者取回任何時間點的程式碼或文件,更重要的是可以做基準(Baseline)與釋出(Release)的管理;教育訓練讓成員學會如何使用開發環境與版本控管,並要求維持紀律,例如Coding Standard,不但減少錯誤,增加程式碼的可讀性,同時也增加溝通的便利性。

為了提昇軟體品質,充分的測試是不可避免的。但是,除了測試以外,Code Review或Design Review也都是提昇品質的好方法,這些方法甚至比測試更能夠在編譯之前找出致命的問題。當然,即便是實施了Code Review或Design Review,測試還是不能省的,而且最好將測試全部自動化,透過單元測試工具(xUnit Test Framework)與持續整合(Continuous Integration)工具,每天進行建置與測試,及早發現錯誤、及早修改,都可以避免專案進行到後期付出更大的除錯成本。

自去年11月以來,每週分享我們的計畫心得,不覺已經過了三個月有餘,終於到了尾聲。這些分享不過是野人獻曝,希望大家未來面對「大長多多」的專案時,都能夠更有效率地達成目標。最後,再一次祝福各位讀者:新春愉快!萬事如意!

作者:陳偉凱、杜秉穎


軟體工程與大型整合專案—以WiMAX整合型計畫為例 (1/3)

留言

這個網誌中的熱門文章

CMMI是什麼?

CRC cards - 非正規物件導向發展技術

談談OCP