官术网_书友最值得收藏!

第1章 軟件架構

1.1 架構的意義

什么是架構?對于架構,每天都有人在耳邊提起。但架構到底是什么,卻很少有人說清楚。

網絡上的解釋包括:軟件架構是一個系統的草圖,軟件架構是構建計算機軟件實踐的基礎,以及軟件架構是一系列相關的抽象模式,用于指導大型軟件系統各個方面的設計等。這些說法都對,但是闡述得還是有些模糊,懂的人本來就懂,不用看,而不懂的人看了還是一頭霧水。

“架構”這個詞太抽象,難以準確定義,而現在的大部分圖書和文章討論的“架構”都是服務器端的部署圖,所以大部分人一提到架構就覺得是幾臺服務器放這里或那里,用不同的軟件連接合作,用各式的框架開發擴展等。

希望這一章可引導讀者認識到,架構無處不在,它實質上是解決生活和工作中問題的一種方案。除了自己著手尋找解決問題的方案外,還可通過其他方法,比如直接購買現成的,或者以外包及部分外包的形式,或者以合作模式等來解決問題,這些都是切實可行的方案,都是值得考慮的方案。我們所求的不是最貴的,也不是最高級的,而是最好用的。

在軟件系統中,架構的重要性不言而喻,項目從研發到上線運營,我們要在不同的方案中選擇合適的架構,例如,前端渲染引擎是自己研發,還是使用商業引擎?商業引擎是使用Unity3D,還是Unreal或其他?具體到怎么用,則要考慮是使用UGUI,還是使用NGUI?UI里的事件系統如何進行統一處理?AI行為算法是選擇行為樹,還是狀態機,抑或是選擇事件型決策樹?數據如何獲取和存儲?場景如何拆分?是否需要將資源分離出去?是使用長連接還是短連接?是選擇TCP還是UDP?服務器端是用C++,還是用Java或Python?是全部使用關系型數據庫,還是加入Cache機制?網絡協議是用Protocol Buff,還是用JSON或XML,抑或是使用完全自定義格式等。

這些項目中的每個子系統都要有自己的方向,把子系統的決策方向合起來再加入它們之間的關聯調用就構成了一個完整的架構,即每個系統、模塊、組件都是軟件系統架構中的一部分。

優秀的架構師不僅需要對每個子系統的決策方向進行深思熟慮,還要結合其他系統以及整體系統需求的方向進行設計。

在架構設計中,為了能夠更好地整理、思考、描述、表達,我們最好使用架構圖。架構師把架構中抽象的系統、模塊、組件畫在圖上,用圓圈、方塊和文字表示,讓自己和大家能夠更加系統地認識架構的意圖、結構,以及子系統的簡要設計。

一個完整的架構圖通常會伴隨些許子系統的細節,或者說子模塊的架構圖。UML對象關系圖就是一種架構圖,它描述了數據類之間的關系,可把系統中的對象用文字和連接圖的方式描述清楚。部署圖也是其中一種架構圖,它把需要多少種服務器、它們分別起到什么作用,以及它們相互之間的關系描述清楚了,而時序圖則把系統程序調用的次序與流程描述清楚了。這些不同角度的架構圖合起來就構成了一個完整的項目架構圖。如果把子系統架構細節略去,在不關心細節的情況下,描述各系統的合作方式,展現給人們的是整體的解決方案,從宏觀的角度看整個項目的布局,會讓人一目了然。

為了讓更多人理解軟件架構,我想把軟件架構形容得更貼近實際生活一些。

可以把軟件架構理解為軟件程序的架子,與現實中的書架有異曲同工之妙,這個架子上有很多大大小小的格子,每個格子里都可以放置固定種類的程序。架子有大有小,大的需要花費點時間去定制,小的則輕便快捷。

架子的大小是由設計師決定的,設計師根據客戶的需求設計,假如放置的空間大,且需要承載的東西多,那么就往容量大的方向設計,讓它能容納更多的東西,能放置各種不同類型的程序,反之則做得簡約些,這樣更容易理解,又輕又快。

架子完成后要拿出去用,如果一出現異常情況就倒了或散架了,就不算是一個好的架子,架子的好壞可從以下幾個方面進行評估。

1.承載力

書架上能放多少東西,能放多重的東西,是使用者(這里使用者可以為客戶、玩家或程序員)比較關注的問題。

從軟件架構的程序意義來說,一個架構能承載多少個邏輯系統,當代碼行數擴展到100萬行時是否依然能夠有序且規范地運行,以及程序員彼此工作的模塊耦合度是否依然能保持原來的設計要求,能夠承載多少個程序員共同開發,共同開發的效率又如何,這是對軟件架構承載力的評定指標。

從架構的目標上來看,對于服務器來說,當前架構能承受多少人同時訪問,能承載的日均訪問量是多少,這就是它承載力的體現。而對于客戶端來說,能顯示多少UI元素,可渲染多少模型(包括同屏渲染和非同屏渲染),則是它的承載力的體現。

若訪問量承載力太低,訪問量一上來就都卡在加載上,大家就不會再有這個耐心來看你的產品了,運營和宣傳部門在導入流量時,效果就會大打折扣。同樣,如果客戶端渲染承載不了多少元素,幀速率過低,畫面卡頓現象嚴重,產品就不會得到認同。

承載力是重要因素,但也并非是唯一因素,綜合因素才是評定好壞的關鍵,一個點的好壞并不能決定全盤的好壞,木桶效應里最短的那塊木板才是產品好壞的關鍵。

2.可擴展性

如果書架上只能放書,這個書架的用途就太單一了,花瓶不能放、箱子不能放、鞋子不能放、袋子不能放、衣服不能放,客戶八成不買單。

架子能適應不同類型的需求,可添加不同類型的系統、不同功能的子系統,是非常必要的。軟件架構也是同樣的,但要具備更多功能就必須有更高的可擴展性。

可擴展性的關鍵在于,是否能在添加新的子系統后不影響或者盡可能少影響其他子系統的運作。假設添加了子系統后,所有系統都需要根據新的子系統重寫或者重構,那就是災難,前面花費的時間、人力、物力和財力全都白費了,這是我們不想看到的,因此可擴展性也是衡量架構的非常重要的指標。

3.易用性

易用性是架構師比較容易忽視的一個點。如果架構師設計了完整的架構,但具體執行時被程序員認為不好用,這時架構師還是執著地推動它的使用,那么團隊間就會加深矛盾,這樣開發效率就會下降。

這就好比在書架上取東西,如果需要先輸入密碼,再打開門,剝去袋子,拿出來,把袋子放進箱子,關上門,當把東西放回書架上時,再來一遍以上所有步驟,這實在是太煩瑣,即使功能再多,承載力再好,使用者也會感到備受煎熬,這樣,開發效率下降是自然的事。甚至有些是機械重復的工作,精力和注意力都耗在了沒有意義的地方,這種情況下,架構師應該適時地改進架構和流程。

易用性決定了架構的整體開發效率,程序員容易上手,子系統容易對接,開發效率自然就高,各模塊、各部件的編寫只需要花一點點精力來關注架構的融合即可,其他精力和注意力都可以集中在子系統的設計和編碼上,這才能讓各系統各盡其職,將效率發揮到極致。

4.可伸縮性

還是用書架比喻,假設現在沒有這么多書和東西要放,房子也不夠大,那么書架如果可以折疊縮小到我需要的大小則會更受用戶歡迎,這就是可伸縮性的體現。

若軟件架構能像我們制造的書架一樣可隨時放大或隨時折疊縮小,那就太好了。當需要的承載量沒有這么大時,可以不使用不需要的功能,化繁為簡,只啟用需要的部分功能,這樣就可以隨時簡化開發流程。

例如,從服務器端的角度,當需要急速導入大量用戶,做到能承載幾百萬人同時在線時,服務器可隨時擴展到幾百上千臺服務器來提高承載量,而在訪問量驟減,或者平時訪問量比較少,甚至低到只有幾十個人訪問時,服務器可縮減到幾臺機子運作,這樣就大大縮減了服務器費用的開銷,可以根據需要隨時變更架構的承載力來節省成本。

從客戶端的角度,伸縮力體現在是否既能適應大型項目上,如上百人協同開發一個復雜系統,也能適應小項目上,如1~3人小團隊的快速開發環境,即小成本小作品的快速迭代。

在實際項目中,有時可伸縮性看起來并不是關鍵的因素,很多人誤認為伸縮性是程序員的負擔,甚至有的項目在某些時期根本不需要可伸縮性,只需要適應當前特定時間的需求即可。這里不得不再次強調可伸縮性在架構中的作用,它是深入理解、設計架構的關鍵因素,是做出優秀的完整架構的重要因素。

5.容錯性以及錯誤的感知力

書架也會有磕磕碰碰的時候,同樣也會出現因某處做工不精導致使用時歪歪斜斜的情況,如果我們保證不了完全沒有問題,至少需要保證它不會因為一點小小的毛病而徹底散架。

軟件架構也是同樣的,軟件中的異常、Bug常有,我們無法預估設備何時損壞。容錯性起到了防止產品在使用中出現錯誤而徹底不能使用的作用,它需要有備份方案自動啟用功能,同時也能夠讓開發人員及時得知問題已經發生,以及問題的所在位置,最好能通過Email或者短信、電話等方式自動通知維護者,并記錄錯誤信息。

從服務器端角度,容錯性包括數據庫容錯性、應用服務器容錯性、緩存服務器容錯性,以及中心服務器容錯性,每個環節出現問題都會通知相關中心服務器改變策略,或者監控服務器檢測得知該服務器出現故障,自動更換成備用服務器或者更換鏈路。

從客戶端角度,容錯性包括當程序發生錯誤時,是否同樣能夠繼續保持運行而不崩潰;當這個頁面程序出錯時,是否依然能夠運行其他程序而不閃退或崩潰。同時所有出現的程序錯誤,都能及時地記錄下來并發送到后臺,存儲為錯誤日志,便于開發人員及時得到詳細的錯誤信息,能夠根據錯誤信息快速找出問題所在。

在架構中,以上這五項能力缺一不可,某項能力特別突出并不能決定整個架構的好壞,要考慮綜合因素。倘若哪一項比較弱,隨著時間的推移,問題會不斷地向該方向聚集,直到最終出現大的問題,甚至崩潰。我們需要一個牢固的、多樣化的、好用的、可伸縮的、有韌性的書架,這也是我們在設計架構時所追求的目標。

萬物相通,木桶原理也適用于架構設計,木桶上僅有一塊或幾塊板比較長沒有用,其他短板照樣撐不住多少水。這契合架構理論,好的架構本來就是由其所有子系統的架構來支撐的,整體架構雖然比其他子系統的架構更具宏觀性,但起不到決定性作用。綜合因素決定成敗,架構也同樣如此。如何讓所有因素都朝著好的方向發展,是架構師最終要思考和解決的問題。

主站蜘蛛池模板: 灵石县| 云浮市| 剑川县| 嘉兴市| 渑池县| 新疆| 霍邱县| 延长县| 钟祥市| 诏安县| 淅川县| 灵璧县| 勃利县| 安远县| 神池县| 东莞市| 莒南县| 辉南县| 阿拉善盟| 自贡市| 白河县| 林周县| 黑河市| 安康市| 阿拉善右旗| 锦州市| 兴海县| 临潭县| 鹿泉市| 遂平县| 临桂县| 大悟县| 颍上县| 苍溪县| 同德县| 哈尔滨市| 来宾市| 辰溪县| 南澳县| 都匀市| 沙雅县|