以 RAIDs 分析做爲架構驅動力

尋找架構驅動力

人類自開始學會以智慧洗亮觀察世界的雙眼以後,就明白觀察事物不能淺嘗輒止停留在表面現象,而要去看透本質。經過本質規律去建模世界,才能以「一」推演萬物。種種推演的過程,皆是要去尋找某種驅動力量做爲分析或建構的起點。緩存

例如,當咱們要分析一個運動中的物體會造成如何的運動軌跡時,就須要尋找產生運動的力,包括初始的動力、重力、摩擦力以及其餘可能干擾物體運動的力。有的力會推進者物體向前,例如初始動力以及與運動方向保持一致的做用力;有的力會阻礙物體的運動,如摩擦力或者空氣阻力等。經過分析這些力的方向及度量,大體能夠描繪出物體可能的運動軌跡。安全

軟件系統的複雜度遠遠超過物體的運動模型(固然,從肯定性角度講,軟件或許比物體的運動更簡單),但其推演的過程倒是類似的,由於一個軟件系統並不是徹底獨立的存在,而是處在一個更大的生態環境圈中,包括客戶的需求與使用體驗、上游依賴系統、下游依賴系統、硬件與網絡環境、團隊技能水平等諸多因素縱橫交錯,顯式或隱式地對軟件架構的走向施加影響。這些影響因素就至關因而影響「軟件」這個物體運動的力量。架構師要作的工做就是要敏銳地從這些紛繁複雜如蛛網通常糾纏的力量中梳理出清晰的脈絡。網絡

所謂「力」,實際上是一種隱喻。雖然觀察軟件系統的視角如萬花筒通常繽紛多彩,然而若從「物理力學」的視角剖析架構,彷佛更加準確直接。軟件系統正如物體通常,在各類影響力之下不停變化(運動)。不一樣的影響因素會決定着架構師的設計決策,而這些決策之間又相互影響着,或者相吸,或者相斥,絕對不能孤立看待。因而,架構分析與設計就變成了對軟件系統的影響力識別,這種設計的驅動力即咱們所謂的RAID分析法架構

RAID分析法

所謂RAID分析法,即識別軟件系統的風險(Risk)、假設(Assumption)、問題(Issue)、依賴(Dependency),準確地說,就是:併發

  • 評估風險
  • 明確假設
  • 分析問題
  • 識別依賴

正如在《架構之美》中John Klein、David Weiss寫道:運維

軟件架構師的首要關注點不是系統的功能。……你關注的是須要知足的品質。品質關注點指明瞭功能必須以何種方式交付,才能被系統的利益相關人所接受,系統的結果包含這些人的既定利益。分佈式

這裏所謂的「品質」,即咱們常說的質量屬性(Quality Attribute)。對於架構師而言,業務需求致使設計複雜度的增長僅僅是一種量的變化;而質量屬性對設計的要求,則可能隨着複雜度的增長而產生質變。以分佈式系統爲例,隨着對消息隊列、分佈式存儲、服務通訊與集成的引入,在數據一致性、可靠性、安全、運維管理等諸多方面,產生的複雜度與單機系統不可同日而語,設計挑戰與難度幾乎與規模造成指數增加。工具

系統複雜度或許是沒有限制的,而人力卻有限。咱們在開始軟件系統的建構與設計時,不免有考慮不周到之處,如果沒有掌握合理的設計方法而深陷浩瀚如滄海通常的各類需求中,牽扯到各個利益相關者的糾纏中,咱們就可能會迷路、困惑,或者做出不適合當下場景的設計決策。性能

RAID分析在必定程度上能夠幫助咱們重拾正確的方向,尤爲在處理質量屬性方面,很有奇效。線程

個人建議是將RAID分析以Workshop的形式開展,召集團隊成員經過頭腦風暴來完成。因爲將全部軟件系統可能面臨的問題分爲了RAID四類,從而明確了討論的範圍與類別,使得參與者可以以更加收斂更加清晰的思路參與進來。一個典型的RAID分析結果以下圖所示:

在進行RAID分析以前,咱們須要明確這四個概念之間的區別。

風險與問題

風險(Risk)與問題(Issue)經常被人混淆在一塊兒,而兩者在概念上卻有其相關性。風險其實就是將來可能出現的問題。咱們在軟件設計的過程當中,一直都在將來與現實中徘徊。知足現實,卻又須要預測將來。然而,將來是不可預測的,全部的預測其實都是一種想象;咱們誇誇其談預測將來,其實不過是想象將來罷了。因而,現實與將來之間就開始了痛苦的拉鋸戰,咱們既不能對將來作過多預測與判斷,卻又不能僅知足於現狀,如何作到架構設計的恰如其分,在規避過分設計的同時,又能讓咱們的架構可以在將來需求發生變化時以最小的成本應對。咱們真正要作到的是前瞻將來,評估風險就是讓咱們可以前瞻將來的瞭望鏡(這世上並無預測將來的魔法水晶球)。

分析如今存在的問題,評估將來風險,將是這場拉鋸戰的關鍵制高點。在斷定優先級時,問題每每高於風險,須要在解決現有問題的前提上,考慮將來風險的應對方案。譬如說,系統目前存在的問題是性能堪憂,那麼除了必要的調優手段外,咱們能夠經過提升系統的可伸縮性來改進性能。然而,要保證系統的可伸縮性,就須要保持服務的無狀態,並在設計系統的各個分層時都須要支持水平擴展,則可能引入數據不一致以及系統欠穩定的風險。

假設

咱們每每會忽略爲系統給定假設(Assumption),而事實上,這種假設每每表明了關鍵的架構約束

架構約束是一種很是重要的驅動力。Roy Fielding在其論文Architectural Styles and the Design of Network-based Software Architectures(《架構風格與基於網絡的軟件架構設計》)中如此勾勒出約束的重要性:

屬性是由架構中的一組約束所致使的。約束每每是由在架構元素的某個方面應用軟件工程原則來驅動的。例如,統一管道和過濾器(uniform pipe-and-filter)風格經過在其組件接口之上應用通用性原則——強迫組件實現單一的接口類型,從應用中得到了組件的可重用性和可配置性的品質。所以,架構約束是由通用性原則所驅動的「統一組件接口」,目的是得到兩個想要獲得的品質,當在架構中實現了這種風格時,這兩個品質將成爲可重用和可配置組件的架構屬性。

咱們在明確假設時,須要將這些約束甄別出來,以之做爲架構設計的驅動力。例如,對於一個移動APP,咱們明確假設:用戶在斷開網絡鏈接時,可以正常地查閱我的信息與產品信息。這個假設就對軟件架構提出約束,即在APP的客戶端須要緩存數據信息,並在用戶鏈接WIFI時,可以自動同步客戶端數據到服務端。

某些假設則是系統功能性的重要約定,好似契約通常,須要在整個設計與實現階段須要聽從。例如假設電商系統須要調用的推薦系統爲第三方系統,那麼在設計時就須要明確推薦系統公開的接口,系統之間如何集成,當推薦系統的服務發生變動時,客戶方該如何應對。這些都會直接影響咱們的設計決策。

依賴

在軟件設計中,咱們無時不刻不在與依賴做鬥爭。依賴自己是無善無惡的,關鍵在於咱們該如何分解(內聚),如何協做(耦合),這就是咱們須要遵循的高內聚低耦合設計原則。在架構層面,狀況更顯複雜,除了系統內部的依賴以外,還須要考慮系統外部上游與下游的依賴。尤爲是跨越物理邊界(能夠視爲一個進程)之間的通訊,會直接影響到可靠性、性能、可伸縮性等諸多質量屬性。

DDD的Context Map定義了九種Bounded Context之間的映射關係,其中包括防腐層、開放主機服務與發佈語言表達的就是Bounded Context之間的集成關係。若是咱們可以在架構之處識別出系統存在的依賴,再結合Cockburn提出的六邊形架構對其進行更加直觀的可視化,找出依賴途經的端口(Port)與適配器(Adapter),而後肯定依賴之間的通訊(集成)方式,幾乎就能夠得出整個軟件系統應用邏輯架構與物理架構的雛形了。

下圖將六邊形架構與識別的依賴結合起來:

實施RAID分析的案例

在多個系統的架構設計或Inception階段,我經過運用RAID分析法驅動系統的軟件架構設計,效果頗佳,雖然在細節處還欠缺精細,但從大處着手,卻能夠幫助咱們高屋建瓴地分析與架構整個系統。如下是針對某版本升級系統的RAID分析案例。

評估風險

一般而言,對風險的識別能夠引導咱們對系統質量屬性的思考,利益相關者可
以充分表達對這些屬性的擔憂,從而驅動咱們去尋找解決方案。

穩定性

在此次RAID分析中,有利益相關者明確提出了對穩定性的擔心。系統的多個模塊駐留在不一樣的節點中,部分模塊仍是以嵌入方式駐留在主控板上。因爲業務須要,模塊之間的通訊相對頻繁,主要的通訊協議爲Telnet與SSH。從舊有的系統表現來看,跨界點之間的通訊在穩定性方面表現欠佳。基於這一問題,咱們在後續的架構設計中對此進行了深刻分析,除了保證通訊實現自身的健壯性與異常處理以外,咱們還決定在主控板一端設計粗粒度的接口,一次性地傳遞版本升級須要的信息,減小沒必要要的通訊。

可擴展性

風險對擴展性的識別,幫助咱們確立了一個架構原則,就是版本規格包的結構不該該影響到主控板的系統。這是由於主控板系統的版本升級受到的制約最多,咱們不但願當產品發生變化時,影響整個版本管理系統。

性能

當須要升級的系統數量較多時,系統的版本升級過程會變得緩慢。而業務需求有要求了系統不能長期處於shutdown狀態,不然會增長運營成本。所以,升級過程一般會選在凌晨,而且要求在較短期內完成整個升級工做,故而性能可謂重中之重。

咱們考慮採用併發方式爲每一個待升級系統進行升級。升級過程是一個獨立的過程,卻又牽涉到較爲複雜的業務流程以及跨節點通訊。因爲部署限制,後臺只能部署在一個JVM之上,經過啓動多個併發線程來處理升級業務。執行升級時,須要加載配置文件到內存中,若同時啓動的線程數過多,則可能致使OutOfMemory異常。這個風險的識別及時地爲咱們敲響了警鐘。咱們爲此安排了技術Spike,以期找到合適的配置項,在性能與可靠性之間進行最優權衡。

明確假設

假設(Assumption)能夠是關鍵的架構約束,也能夠是系統功能性的約定。架構約束既多是設計的阻力,也能夠成爲動力。通過討論,咱們基本上肯定了兩條最爲重要的假設:

  • 系統必須支持雙向兼容。這個假設的提出,則要求咱們在開發過程當中,只要接口已經發布,就不能再修改接口。除修復缺陷外,咱們不能刪除舊有功能,只能增長新功能。即便舊有功能已被新功能取代,爲保持兼容性,咱們也不能刪除,但能夠將其置爲@deprecated標註。
  • 版本升級過程當中,若先後操做具備依賴關係,則必須保證事務的一致性,要麼所有成功,要麼所有失敗。事實上,這一條假設也是對質量屬性「可靠性」的一個迴應。

分析問題

整個RAID的識別都針對技術層面,而非管理層面。所以咱們識別的問題也限
制在技術範圍。

在咱們識別出來的問題中,最致命的一個問題是關於模塊NVUM的加載。NVUM是一個JAR包。它並不是一個獨立運行的系統,而是由管理系統動態加載。之因此選擇動態加載,而非靜態依賴,緣由包括:

  • NVUM由咱們項目組維護,管理系統則屬於另一個項目,兩邊的版本計劃徹底不一致。網管系統爲一個Client-Server系統,相對成熟,目前已被獨立地部署到全球多個外場。若採用靜態依賴,就須要咱們將其歸入到網管系統中。但NVUM的版本更新更加頻繁,外場不可能由於NVUM一個模塊的調整,而付出頻繁更新管理系統的代價。
  • 管理系統負責監控外場各設備的運轉情況。雖然系統的重啓(耗時數十分鐘)並不會影響設備的功能,但卻可能在重啓過程當中,由於未能及時掌控設備狀態,而致使沒法及時發現問題。必須避免這種事故的發生。換言之,管理系統的重啓代價過高,不能常常重啓。

JAR包的動態加載能夠經過URLClassLoader來實現,又或者選擇OSGI。前者須要充分驗證其穩定性,後者則過於重型,成本過高。另外,動態加載方式對於模塊設計而言存在設計約束,即咱們須要將NVUM分爲interface和impl兩個模塊,且必須保證interface的穩定性。

另外一個方案是採用腳本,例如選擇可以運行在JVM上的Groovy腳本語言。咱們只須要在Java中調用Groovy提供的GroovyShell,就能直接讀取groovy腳本文件;而後調用run()方法便可執行腳本。

識別依賴

除了NVUM與管理系統,NVUM與主控板,主控板與其餘設備之間的依賴外,牽涉到的依賴還有不少。有的屬於輸入依賴,有的則屬於輸出依賴。此外,還有版本製做工具等系統也會受到NVUM的影響。同時,NVUM還須要訪問內建的文件系統,經過FTP讀取諸多外部文件。通訊則可能採用Telnet、SNMP、SSH等多種協議。

這些依賴的識別便於肯定本系統對其餘系統可能形成的影響,事先識別有利於咱們及時作好溝通,同時還須要就一些架構約定以及接口定義達成一致意見。依賴的識別也有利於咱們設計系統的物理架構,考慮系統的部署方式。

相關文章
相關標籤/搜索