萬字長文探討可信構(gòu)架之道(上) 萬字長文探討可信構(gòu)架之道(下)
架構(gòu)是什么?簡單來說就是架子的結(jié)構(gòu),譬如動物、人的骨架,建筑物的結(jié)構(gòu)框架等。架構(gòu)影響什么?架構(gòu)會影響最終物品的形態(tài)和質(zhì)量。類比軟件系統(tǒng)亦如此,在這個二進制的世界里,程序員扮演著上帝的角色,為這個世界創(chuàng)造出不同的東西。那么,作為根本,重要性不言而喻,這就需要我們思考如何合理地進行物體設(shè)計、架構(gòu)設(shè)計。
軟件技術(shù)領(lǐng)域的發(fā)展歷程也是系統(tǒng)架構(gòu)的演變過程,從單體、分布式系統(tǒng)、SOA架構(gòu),再到更細粒度的微服務(wù)化、服務(wù)網(wǎng)格、云原生架構(gòu)等。架構(gòu)設(shè)計的初衷也是為了應(yīng)對各類場景的特有的高并發(fā)、高性能、穩(wěn)定、易維護及可擴展性等。架構(gòu)是多維的,可以有多種方法對其進行描述。架構(gòu)設(shè)計依賴人的思考和判斷,是一種抽象的結(jié)構(gòu),它由軟件的各個組成部分和這些部分之間的依賴關(guān)系構(gòu)成。經(jīng)驗豐富的設(shè)計人員能把握更多構(gòu)架細節(jié),對于程序架構(gòu)具有較強的可預(yù)見性,讓設(shè)計更加合理、充分。
軟件系統(tǒng)架構(gòu)對功能性需求影響不大,但架構(gòu)的優(yōu)劣決定了系統(tǒng)的非功能性需求即質(zhì)量屬性或簡稱為“能力”,決定了系統(tǒng)品質(zhì)的好壞,決定了軟件交付的可靠、可測試、可維護、可擴展及可部署性等。事實上,在任何架構(gòu)甚至是一團糟的架構(gòu)之上,都可以實現(xiàn)應(yīng)用的功能性需求。因此即便是成功的系統(tǒng),其內(nèi)部架構(gòu)也可能往往是一個大泥球,不合理的架構(gòu)通常導(dǎo)致系統(tǒng)緊耦合、玻璃心、難以改變、沒有頭緒,甚至關(guān)于架構(gòu)的規(guī)模如何?系統(tǒng)的性能如何?程序容易修改嗎?系統(tǒng)的部署模型是怎么樣?系統(tǒng)的響應(yīng)如何?一切都是那么難以回答、難以定論 … …
一. 介 紹
軟件架構(gòu)設(shè)計可從宏觀上說明系統(tǒng)的組成與特性,是經(jīng)系統(tǒng)性思考、權(quán)衡之后在現(xiàn)有資源約束下的最合理決策, 最終明確的系統(tǒng)骨架包括子系統(tǒng)、模塊、組件,以及它們之間的協(xié)作關(guān)系、約束規(guī)范、指導(dǎo)原則。覆蓋需求、技術(shù)棧、成本、組織及架構(gòu)、可擴展性、可維護性等。
1) 系統(tǒng)性思考的合理決策:比如技術(shù)選型、解決方案等;
2) 明確的系統(tǒng)骨架:明確系統(tǒng)有哪些部分組成;
3) 系統(tǒng)協(xié)作關(guān)系:各個組成部分如何協(xié)作來實現(xiàn)業(yè)務(wù)請求;
4) 約束規(guī)范和指導(dǎo)原則:保證系統(tǒng)有序,高效、穩(wěn)定運行。
軟件架構(gòu)設(shè)計要完成兩項工作,一是分析,二是設(shè)計。分析是分析需求,設(shè)計則是設(shè)計軟件的大致結(jié)構(gòu)。很多的方法論把分析和設(shè)計兩種活動分開,但其實兩者是很難區(qū)分的,做分析的時候會想到如何設(shè)計,而思考如何設(shè)計反過來又會影響分析的效果??梢哉f,兩者之間是相互聯(lián)系和不斷迭代的。
在架構(gòu)設(shè)計范疇中對于業(yè)務(wù)的理解和經(jīng)驗積累同樣重要,當(dāng)短期的需求與整體設(shè)計沖突時,有些設(shè)計師會偏向于當(dāng)前的設(shè)計,缺乏對整體的思考,對未來的思考,而腐化了系統(tǒng)整體應(yīng)有的架構(gòu)。當(dāng)程序員跟產(chǎn)品經(jīng)理開撕,這個需求不能做,那個需求調(diào)整需要很長時間。其中也折射出架構(gòu)設(shè)計的不合理可能。
二. 架構(gòu)即未來
軟件架構(gòu)是一個系統(tǒng)開發(fā)生命周期中最前端的部分,也是最關(guān)鍵、核心的部分。它決定了后續(xù)代碼的走向,決定了項目的走向,有時候甚至能決定一家公司的成與敗。架構(gòu)的目標是保證系統(tǒng)的高可用、可擴展、可伸縮、安全等一系列的指標,好的開始相當(dāng)于成功了一半。
軟件架構(gòu)是系統(tǒng)的頂層結(jié)構(gòu),要著眼于全局,包括硬件、操作系統(tǒng)、網(wǎng)絡(luò)環(huán)境,以及從立項到維護之間的所有過程(需求、設(shè)計、編碼、部署、維護及迭代)。架構(gòu)決定子系統(tǒng)之間的關(guān)系、分層與通訊方式、公共設(shè)計原則/風(fēng)格、功能需求與非功能需求的優(yōu)先級與取舍原則等。架構(gòu)是多種結(jié)構(gòu)的體現(xiàn),就像建筑物的結(jié)構(gòu)會隨著觀察動機和出發(fā)點的不同而有多種含義一樣,軟件構(gòu)架也表現(xiàn)為多種結(jié)構(gòu),常見的軟件結(jié)構(gòu)有:模塊結(jié)構(gòu)、邏輯或概念結(jié)構(gòu)、進程或協(xié)調(diào)結(jié)構(gòu)、物理結(jié)構(gòu)、使用結(jié)構(gòu)、調(diào)用結(jié)構(gòu)、數(shù)據(jù)流、控制流、類結(jié)構(gòu)等。
優(yōu)秀的架構(gòu)可促進項目的良性發(fā)展,設(shè)計之初將遠的近的都考慮進來,最后會使得項目朝著好的方向發(fā)展,降低項目所投入的時間、金錢、人力成本。壞的角度來看,對于多變的軟件而言,有一句話說的好,計劃跟不上變化。需求不確定性與人,也與業(yè)務(wù)相關(guān)。架構(gòu)的變化往往成本很高,好的架構(gòu)可使變化發(fā)生在局部而不影響整個系統(tǒng)。架構(gòu)和設(shè)計的可擴展決定了需求變更、業(yè)務(wù)增加時付出的成本;標準化、規(guī)范化、可傳承有利于提升團隊的效率;“播種,施肥,除草”,播種時的付出會影響除草的成本,而這個成本可能是數(shù)倍甚至數(shù)十倍的,把初始做的更好后面才能更省心。
架構(gòu)的分類上可分為業(yè)務(wù)架構(gòu)、應(yīng)用架構(gòu)、技術(shù)架構(gòu),部署架構(gòu)。業(yè)務(wù)架構(gòu)是生產(chǎn)力,應(yīng)用架構(gòu)是生產(chǎn)關(guān)系,技術(shù)架構(gòu)是生產(chǎn)工具。業(yè)務(wù)架構(gòu)決定應(yīng)用架構(gòu),應(yīng)用架構(gòu)需要適配業(yè)務(wù)架構(gòu)并隨之不斷進化,同時依托技術(shù)架構(gòu)、部署架構(gòu)而最終落地。
1. 架構(gòu)設(shè)計的目標
架構(gòu)設(shè)計的目的是為了解決軟件系統(tǒng)的復(fù)雜度帶來的問題,對系統(tǒng)的高可用、高性能、可擴展、安全、伸縮性、簡潔等做系統(tǒng)級的把握。常規(guī)來說功能需求決定業(yè)務(wù)構(gòu)架,非功能需求決定技術(shù)構(gòu)架,變化案例決定構(gòu)架的范圍。功能需求定義了軟件能做什么,根據(jù)業(yè)務(wù)需求來設(shè)計業(yè)務(wù)構(gòu)架,以使得未來的軟件能夠滿足客戶的需要。非功能需求定義了一些性能、效率上的約束、規(guī)則,技術(shù)構(gòu)架要能夠滿足這些約束和規(guī)則。變化案例是對未來可能發(fā)生的變化估計,結(jié)合功能和非功能需求就可確定一個需求的范圍,進而確定一個構(gòu)架的范圍。
結(jié)構(gòu)面的考慮因素
1) 需求的符合性:正確性、完整性;功能性需求、非功能性需求;
2) 總體性能(內(nèi)存,數(shù)據(jù)組織和內(nèi)容,任務(wù),網(wǎng)絡(luò)操作,關(guān)鍵算法,硬件和其他接口影響);
3) 運行可管理性: 便于控制系統(tǒng)運行,監(jiān)視狀態(tài),錯誤處理,模塊間通信與可維護性;
4) 與其他系統(tǒng)接口兼容性;
5) 與網(wǎng)絡(luò)、硬件接口兼容性及性能;
6) 系統(tǒng)安全性;
7) 系統(tǒng)可靠性;
8) 業(yè)務(wù)流程及信息的可調(diào)整性;
9) 使用方便性;
11)架構(gòu)樣式的一致性.
注:運行時負載均衡可以從系統(tǒng)性能、可靠性方面考慮。
架構(gòu)設(shè)計的目標
確定系統(tǒng)邊界,確定系統(tǒng)在技術(shù)層面上的做與不做。
確定系統(tǒng)內(nèi)模塊之間的關(guān)系,依賴關(guān)系及模塊的宏觀輸入與輸出。
確定指導(dǎo)后續(xù)設(shè)計與演化的原則,使后續(xù)子系統(tǒng)或模塊設(shè)計在規(guī)定的框架內(nèi)繼續(xù)演化。
確定非功能性需求目標,涉及如下:
可靠性:系統(tǒng)對于用戶的商業(yè)經(jīng)營和管理來說極為重要,因此必須非??煽?。
安全性:軟件系統(tǒng)所承擔(dān)的交易的商業(yè)價值極高,系統(tǒng)的安全性非常重要。
可擴展:? 在用戶的使用率、用戶數(shù)快速增長下保持合理的性能,適應(yīng)用戶的市場擴展可能性,以及包括對系統(tǒng)進行功能和性能的擴展。
可定制:? 同樣的一套軟件,可以根據(jù)客戶群的不同和市場需求的變化進行調(diào)整。
可維護:? 一是排除現(xiàn)有錯誤,二是將新的需求反映到現(xiàn)有系統(tǒng)中去。一個易于維護的系統(tǒng)可以有效地降低技術(shù)支持的費用。
客戶體驗:軟件系統(tǒng)必須易于使用。
市場時機:以最快速度爭奪市場先機非常重要。
組織結(jié)構(gòu)方面
1) 開發(fā)可管理性:便于人員分工、利于配置管理、大小的合理性與適度復(fù)雜性;
2) 可維護性:與運行可管理性不同;
3) 可擴充性:系統(tǒng)方案的升級、擴容、擴充性能;
4) 可移植性:不同客戶端、應(yīng)用服務(wù)器、數(shù)據(jù)庫管理系統(tǒng);
5) 需求的符合性.
2. 設(shè)計的思維模式
架構(gòu)設(shè)計的本質(zhì)是管理復(fù)雜性。抽象、分層、分治和演化思維是架構(gòu)設(shè)計師征服復(fù)雜性的四種根本性手段。
A.抽象化設(shè)計思維
抽象是對某種事物進行簡化表示或描述的過程,抽象讓我們關(guān)注要素,隱藏額外細節(jié)。抽象能力的強弱,直接決定我們所能解決問題的復(fù)雜性和規(guī)模大小。架構(gòu)設(shè)計應(yīng)摒棄具體細節(jié),抓住軟件最上層、優(yōu)先級最高、風(fēng)險最大的那部分需求。
合理地使用抽象可提升設(shè)計的簡單性,改善軟件開發(fā)的質(zhì)量。通常使用到兩種抽象方法,基于過程的抽象和基于數(shù)據(jù)的抽象。基于過程做抽象時,會將待解決的問題分解為一個個小的子問題,每一個子問題分別由一個獨立的模塊、函數(shù)、類等來完成。良好抽象的系統(tǒng)在其中某一個部分的實現(xiàn)被替換的情況下,不需要修改設(shè)計仍然能正常工作。基于設(shè)計良好的抽象可組合構(gòu)建出功能更加強大和復(fù)雜的系統(tǒng)?;跀?shù)據(jù)抽象的方法可以將復(fù)雜數(shù)據(jù)結(jié)構(gòu)的使用和其構(gòu)造分離,通過使用“抽象數(shù)據(jù)”的方式,用戶可以通過明確定義的一系列接口對其進行訪問和操作,隱藏對象的內(nèi)部特征,對外部環(huán)境透明。而不適合問題本質(zhì)的抽象方式不僅會影響軟件設(shè)計的簡單性,也可能給軟件的可維護性帶來負面影響。
B.分層的設(shè)計模式
分層技術(shù)在計算機領(lǐng)域中有著悠久歷史,分層優(yōu)勢在于:上層的邏輯無需了解所有底層邏輯,它只需要了解和它鄰接層的細節(jié)。TCP/IP協(xié)議棧就是通過不同的層對數(shù)據(jù)進行層層封包,不同層間的耦合度明顯降低。通過嚴格的區(qū)分層次,大大降低了層間耦合度。分層原則對軟件進行結(jié)構(gòu)上的劃分,定義了結(jié)構(gòu)的不同部分的職責(zé),總體思路并無特別,只是將系統(tǒng)進行有效組織的方式,在完成分層之后軟件架構(gòu)已經(jīng)清晰化了。
為了構(gòu)建一套復(fù)雜系統(tǒng),可把整個系統(tǒng)劃分成若干個層次,每一層專注解決某個領(lǐng)域的問題,并向上提供服務(wù)。有些層次是縱向的,它貫穿所有其它層次,稱為共享層。分層也可以認為是抽象的一種方式,將系統(tǒng)抽象分解成若干層次化的模塊。
C.問題分治化思維
分而治之也是應(yīng)對和管理復(fù)雜性的一般性方法。對于一個無法一次解決的大問題,一般先把大問題分解成若干子問題,如果子問題還無法直接解決,則繼續(xù)分解成子子問題,直到可以直接解決的程度,這個是分解(divide)的過程;然后將子子問題的解組合拼裝成子問題的解,再將子問題的解組合拼裝成原問題的解,這個是組合(combine)的過程。
D.演化式架構(gòu)思維
架構(gòu)既是設(shè)計出來的,同時也是演化出來的,在設(shè)計中演化,在演化中設(shè)計,一個不斷迭代的過程。架構(gòu)師除了要利用自身的架構(gòu)設(shè)計能力,同時也要學(xué)會借助用戶反饋和進化的力量,推動架構(gòu)的持續(xù)演進,這個就是演化式架構(gòu)思維。能夠不斷應(yīng)對環(huán)境變化的系統(tǒng),才是有生命力的系統(tǒng),架構(gòu)的好壞,很大部分取決于它應(yīng)對變化的靈活性。所以具有演化式思維的架構(gòu)師,能夠在一開始設(shè)計時就考慮到后續(xù)架構(gòu)的演化特性,并且將靈活應(yīng)對變化的能力作為架構(gòu)設(shè)計的主要考量。
軟件架構(gòu)需根據(jù)業(yè)務(wù)的發(fā)展而不斷變化。如果沒有把握這個本質(zhì),在做架構(gòu)設(shè)計時就很容易陷入試圖一步到位的誤區(qū),期望不管業(yè)務(wù)如何變化架構(gòu)都穩(wěn)如磐石。業(yè)務(wù)的發(fā)展和變化總是很快,實踐中遵循演化優(yōu)于一步到位的原則。實踐中可以參考:首先設(shè)計出的架構(gòu)要滿足當(dāng)時的業(yè)務(wù)需要。其次,架構(gòu)要不斷地在實際應(yīng)用過程中迭代,保留優(yōu)秀的設(shè)計,修復(fù)有缺陷的設(shè)計,改正錯誤的設(shè)計,去掉無用的設(shè)計,使得架構(gòu)逐漸完善。當(dāng)業(yè)務(wù)發(fā)生變化時,架構(gòu)要擴展、重構(gòu),甚至重寫;代碼也許會重寫,但有價值的經(jīng)驗、教訓(xùn)、邏輯、設(shè)計等卻可以在新架構(gòu)中延續(xù)。嚴格的對待每一次的迭代,確保計劃的完成、確保軟件的質(zhì)量、確保用戶的需求得到滿足,這樣才是正統(tǒng)的迭代之路。
最開始的設(shè)計一定只是一個原始架構(gòu),但對于后續(xù)的架構(gòu)設(shè)計而言非常重要。迭代設(shè)計也可稱之為增量設(shè)計,每一次的迭代都是在上一次迭代的基礎(chǔ)上進行的,迭代將致力于重用、修改、增強目前的架構(gòu),以使架構(gòu)越來越強壯,得到一個穩(wěn)定的架構(gòu)。
3. 典型設(shè)計實踐
軟件模式通過定義一組互相協(xié)作的軟件元素來解決軟件架構(gòu)設(shè)計問題,是實現(xiàn)架構(gòu)設(shè)計的具體手段。架構(gòu)設(shè)計需以全局的視角統(tǒng)籌計算、數(shù)據(jù)、存儲、通訊等來綜合考慮與權(quán)衡。
架構(gòu)模式有助于定義程序的基本特征和行為。譬如對于系統(tǒng)結(jié)構(gòu)設(shè)計使用層模式,對于分布式系統(tǒng)使用代理模式,對于交互系統(tǒng)使用MVC模式。模式本就是針對特定問題的解,一些架構(gòu)模式很自然讓程序適應(yīng)大規(guī)模,有些讓程序變得靈巧敏捷??山Y(jié)合需求的特點和目標來采用相應(yīng)模式設(shè)計架構(gòu)。設(shè)計模式是從代碼層面提煉出來的一種總結(jié),可使代碼的耦合度達到最大限度的分離,從而可使代碼更好的被復(fù)用,更易被替換,更好的擁抱需求的變化。
1) 設(shè)計簡單化原則
【Keep It Simple】"簡單要比復(fù)雜有效",這就是簡單設(shè)計模式的基本思路。一個復(fù)雜的架構(gòu)不論是測試還是維護、以及后期的開發(fā)迭代都是困難的,也會導(dǎo)致溝通成本的上升,架構(gòu)應(yīng)盡可能的簡單明了,用最簡單的方案來解決問題。
無論是結(jié)構(gòu)還是邏輯的復(fù)雜都會存在各種問題。架構(gòu)越簡單,穩(wěn)定性就越好,這也是行業(yè)的共識。簡單的架構(gòu)并不等于實現(xiàn)簡單,簡單的架構(gòu)需要設(shè)計者花費大量的心血,也要求設(shè)計者對技術(shù)有很深的造詣。簡單的架構(gòu)設(shè)計有助于加快開發(fā)團隊對架構(gòu)的理解。簡單意味著問題不會非常的復(fù)雜,架構(gòu)是解決需求的關(guān)鍵,無論需求再怎么復(fù)雜多變,總可以找出簡單穩(wěn)定的部分,以穩(wěn)定的部分作為基礎(chǔ),再根據(jù)需要進行改進擴展,以解決復(fù)雜的問題。其次,簡單性還體現(xiàn)在表示的簡單上。也體現(xiàn)在系統(tǒng)內(nèi)層次、模型、動靜、流量、鏈路、讀寫、時間(異步化)等不同維度、粒度上的抽象、設(shè)計的簡化。
“彈性的設(shè)計”滿足需求變更的背后往往所付出的是復(fù)雜的設(shè)計。解決問題的大方向應(yīng)是將復(fù)雜問題簡單化,但在具體實施設(shè)計過程中,很多人可能會將簡單問題復(fù)雜化,在設(shè)計模式的運用上易犯這個錯誤,如何盡可能的做到設(shè)計的簡單明,落實到每個類/模塊的具體實現(xiàn)上要真正能體現(xiàn)系統(tǒng)事物的本質(zhì),本質(zhì)特征只有一個,代碼越接近它,表示設(shè)計就是簡單明了,越簡單明了,承載的系統(tǒng)就越穩(wěn)定、可靠、可信。
2) 高內(nèi)聚、低耦合
高內(nèi)聚、低耦合是軟件工程中的概念,是判斷設(shè)計好壞的標準,是架構(gòu)設(shè)計最主要的目標。具有更好的重用性、維護性及可擴展性,軟件設(shè)計原則是其實現(xiàn)的指導(dǎo)方針。設(shè)計中通常用耦合度和內(nèi)聚度作為衡量模塊獨立程度的標準,劃分模塊的一個準則是高內(nèi)聚低耦合。從模塊來看,高內(nèi)聚是盡可能使類的每個成員方法只完成一件事,低耦合是減少類內(nèi)部一個成員方法調(diào)用另一個成員方法。從類的角度看,是減少類內(nèi)部對其他類的調(diào)用。從功能模塊來看,是減少模塊之間的交互復(fù)雜度即橫向:類與類之間、模塊與模塊之間,縱向:層次之間;盡可能內(nèi)容內(nèi)聚,數(shù)據(jù)耦合。
降低依賴,解除耦合
反向依賴
只從上往下依賴,將公共的重復(fù)功能的模塊抽取出來。公共模塊必須足夠的功能單一,不能有其他業(yè)務(wù)的邏輯判斷在里面。在整個模塊依賴關(guān)系里,應(yīng)該是一棵樹狀結(jié)構(gòu)的關(guān)系圖,而不是一個網(wǎng)狀的關(guān)系圖。
配置解耦
每個模塊的動態(tài)屬性,設(shè)定配置項,可使用配置中心進行實時更新并生效。
權(quán)限解耦
功能的權(quán)限控制及安全驗證,原來是與業(yè)務(wù)代碼集成。在服務(wù)網(wǎng)格化架構(gòu)中,權(quán)限功能劃分歸屬到通訊邊車模塊,業(yè)務(wù)和技術(shù)面通過API接***互。
流量解耦
服務(wù)網(wǎng)格架構(gòu)下,流量的控制下沉到邊車通訊模塊。支持流量拆分,服務(wù)之間的流量支持隔離性。
數(shù)據(jù)解耦
保證模塊之間的數(shù)據(jù)不相互影響,同一個模塊的冷熱數(shù)據(jù)解耦。系統(tǒng)運行時間長了后也會積累大量的數(shù)據(jù),為保證系統(tǒng)的性能穩(wěn)定,要減少數(shù)據(jù)量太大造成的性能降低。
擴容解耦
好的架構(gòu)設(shè)計需具備橫向擴展能力,只通過增加硬件的方式就能提高系統(tǒng)性能。
部署解耦
支持快速試錯、灰度發(fā)布。同一個模塊先部署升級幾臺服務(wù)器到新版本,重啟完成后流量切入后即可驗證當(dāng)前的部署是否有問題,無問題就繼續(xù)部署其他的節(jié)點,如有問題則馬上回滾到上一個版本。
動靜解耦
當(dāng)同一個模塊的瞬間有非常高并發(fā)時,純粹的流量解耦仍不夠。不能讓前端流量沖擊后面真正的關(guān)鍵處理功能,需要更細的流量解耦,實現(xiàn)靜態(tài)、動態(tài)資源訪問分離。
可靠的系統(tǒng)是高度模塊化(不暴露無關(guān)接口),最小侵入(常規(guī)使用無需繼承之類的強耦合關(guān)系),易集成到完全不同類型的代碼庫(比如盡可能使用可移植代碼而非直接調(diào)用平臺API),對外部環(huán)境有極少的假定,極力與其他系統(tǒng)的實現(xiàn)細節(jié)解耦。
3) 接口的設(shè)計原則
接口,可以理解為契約,一種約定。系統(tǒng)各個模塊之所以能組合工作,正是因為通過定義好的API來交互。在對外提供抽象API的同時,也可能需使用其他模塊的API作為自身運行的基礎(chǔ)。接口設(shè)計要職責(zé)單一,盡可能隱藏內(nèi)部實現(xiàn),避免通過繼承導(dǎo)致行為擴散,命名及風(fēng)格統(tǒng)一,增強可理解性,定義好版本等保障接口穩(wěn)定性、兼容性,對接口進行驗證的思路是保證接口的可測試性。
a)? 封裝原則
優(yōu)秀的接口設(shè)計會隱藏實現(xiàn)的細節(jié),僅把需要的接口呈現(xiàn)給關(guān)聯(lián)方,而具體的實現(xiàn)則對外部透明。
b)? 最小職責(zé)
一個類實現(xiàn)的功能應(yīng)盡可能緊湊,只處理緊密相關(guān)的功能,一個方法更應(yīng)該只做一件事情,需要設(shè)計人員發(fā)現(xiàn)類的不同職責(zé)并將其分離。譬如一個微服務(wù)應(yīng)盡可能職責(zé)單一,提供的接口也盡可能單一。
c)? 最小接口
暴露給用戶使用的方法應(yīng)盡可能的少,公布的方法可能被客戶頻繁使用,如設(shè)計上存在問題或是要進行改進,都會對現(xiàn)有方法造成影響,因此需要將這些影響減到最小。另外,一些較輕型的共有方法應(yīng)組合為單個的方法,降低用戶和系統(tǒng)的耦合程度,具體實現(xiàn)可以通過外觀模式或委托模式。
d)? 最小耦合
設(shè)計的類和其它類的交互應(yīng)該盡可能的少,如果發(fā)現(xiàn)一個類和大量的類存在耦合,可以引入新的類來削弱這種耦合度。在設(shè)計模式中,中介模式和外觀模式都是此類型的應(yīng)用。
e)? 分層原則
封裝原則的提升。一個系統(tǒng),往往有各種各樣的職責(zé),如有負責(zé)和DB打交道的代碼,也有和用戶打交道的代碼,把這些代碼根據(jù)功能劃分為不同的層次,就可對軟件架構(gòu)的不同部分實現(xiàn)大的封裝。
參考博客:【SOLID原則精解之接口隔離原則 ISP】
4) 最少知識原則
一種面向?qū)ο蟪绦蛟O(shè)計的指導(dǎo)原則,描述了一種保持代碼松耦合的策略。每個單元對其他單元只擁有有限的知識,只了解與當(dāng)前單元緊密聯(lián)系的單元;為軟件設(shè)計帶來了兩個主要的益處:更好的信息隱藏和更少的信息重載。打個比方,人可以命令一條狗行走,但是不應(yīng)該直接指揮狗的腿行走,應(yīng)該由狗去指揮它的腿行走。應(yīng)用該原則有利于降低模塊間的耦合,提升軟件的可維護性和可重用性,但可能會導(dǎo)致不得不在類中設(shè)計出很多用于中轉(zhuǎn)的包裝方法,提升類設(shè)計的復(fù)雜度。
5) 門面控制模式
把職責(zé)賦予系統(tǒng)、設(shè)備或子系統(tǒng)的表示類(門面控制器),或者某個用例的表示類,讓控制器接收事件并協(xié)調(diào)整個系統(tǒng)的運作。兩個或多個對象間有交互的情況下,為避免直接耦合,提高重用性,創(chuàng)建中間類并賦予職責(zé),對象的交互交由中間類協(xié)調(diào)。系統(tǒng)之間亦類同。
外觀模式,可將調(diào)用者和復(fù)雜的業(yè)務(wù)類隔離,調(diào)用者無須知道業(yè)務(wù)類之間的復(fù)雜關(guān)系就能進行業(yè)務(wù)處理,從而大大降低了調(diào)用者和業(yè)務(wù)類之間的耦合度,適合用在內(nèi)部關(guān)系較為復(fù)雜的組件中,也適合用在業(yè)務(wù)層向表示層發(fā)布接口的情況中。通過參數(shù)來實現(xiàn)數(shù)據(jù)的傳遞。
6) 信息專家模式
為子系統(tǒng)/模塊分配職責(zé)的通用參考原則,把職責(zé)分配給擁有足夠信息可以履行職責(zé)的專家,形象化來說就是將數(shù)據(jù)、事件、狀態(tài)等傳遞給承擔(dān)該數(shù)據(jù)處理的單一職責(zé)模塊進行處理,可作為職責(zé)邊界的劃分依據(jù)。合理的職責(zé)分配能力,也就是每個類/組件/子系統(tǒng)應(yīng)該承擔(dān)什么職責(zé),如何保證職責(zé)單一,它們之間如何協(xié)作,需要職責(zé)邊界清晰。打個比方,當(dāng)不確定哪個團隊應(yīng)該負責(zé)某個微服務(wù)時,一般原則也是誰擁有數(shù)據(jù)誰負責(zé),基于有界上下文(一般是邊界比較清晰的領(lǐng)域數(shù)據(jù)源)構(gòu)建微服務(wù)。
7) 數(shù)據(jù)的一致性
分布式體系結(jié)構(gòu)中,要保證數(shù)據(jù)的一致性,可用性和分區(qū)容忍性。但最多同時滿足這三項中的兩項,BASE理論思想是即使無法做到數(shù)據(jù)的強一致性,但可采用適當(dāng)方式達到最終一致。
軟狀態(tài)是指允許系統(tǒng)存在中間狀態(tài),而該中間狀態(tài)不會影響系統(tǒng)的整體可用性。分布式存儲中一般單份數(shù)據(jù)至少存三副本,允許不同節(jié)點間副本同步的延遲就是軟狀態(tài)的體現(xiàn)。系統(tǒng)中的所有數(shù)據(jù)副本經(jīng)過一段時間后最終能達成一致狀態(tài)為最終一致性。弱一致性和強一致性相反,最終一致性是弱一致性的一種特殊情況。
微服務(wù)體系架構(gòu)中,狀態(tài)管理也許會成為首要和中心的問題。提供連接性和集成意味著系統(tǒng)本質(zhì)上要么是查詢狀態(tài),要么是更改狀態(tài)(或兩者)。對于給定的實體或信息,查詢和更改狀態(tài)的方法通常并不唯一。為避免數(shù)據(jù)損壞或意外結(jié)果,顯式聲明狀態(tài)并使用某種策略來處理更改和查詢狀態(tài)帶來的副作用,通過這種策略,每個微服務(wù)組件可以實現(xiàn)更高物理級別上的自治,從而允許更快的更改速度。
8) 穩(wěn)定、可靠原則
穩(wěn)定、可靠性是系統(tǒng)在給定的時間間隔及給定環(huán)境條件下,按設(shè)計要求,成功地運行程序的概率。成功地運行不僅要保證系統(tǒng)能正確地運行,滿足功能需求,還要求當(dāng)系統(tǒng)出現(xiàn)意外故障時能夠盡快恢復(fù)正常運行,數(shù)據(jù)不受破壞。架構(gòu)設(shè)計及部署上要考慮N+1冗余度(中心/服務(wù)器/系統(tǒng)/中間件/服務(wù)/數(shù)據(jù)等),避免單點問題故障、宕機。面向服務(wù)化的系統(tǒng)架構(gòu)可通過服務(wù)治理,譬如限流、降級等來保障系統(tǒng)的可靠、穩(wěn)定運行, 要實現(xiàn)隔離故障設(shè)計,通過斷路保護避免故障傳播和交叉影響。
服務(wù)方面,網(wǎng)絡(luò)拓撲中任意節(jié)點丟失都有可能導(dǎo)致服務(wù)不可用,如邊緣系統(tǒng)能提前檢測到具有高風(fēng)險的節(jié)點那么就可避免。系統(tǒng)角度,節(jié)點之間能互通狀態(tài)和診斷信息,使得在系統(tǒng)層面部署故障檢測、節(jié)點替換、數(shù)據(jù)檢測等十分方便。數(shù)據(jù)角度,指數(shù)據(jù)通信方面等的可靠性。
9) 可擴展、伸縮性
對擴展的渴求源于需求的易變,需要架構(gòu)具有一定的擴展性以應(yīng)對變化,適應(yīng)未來可能的變化。但擴展性和穩(wěn)定性,擴展性和簡單性都存有矛盾面,因此需要權(quán)衡投入、產(chǎn)出比,以設(shè)計出具有適當(dāng)延展性的架構(gòu),譬如系統(tǒng)能做到無縫升級、擴容、擴充的可行性和便利等。好的系統(tǒng)架構(gòu)能獲得低延遲和高吞吐量,擴展性的目標是用可接受的延遲獲得最大的吞吐量。
擴展包含兩個層面:一是功能的可擴展性,主要是針對平臺框架是否設(shè)計并預(yù)留了足夠的擴展點,后續(xù)可以方便的增加各種功能或有第三方實現(xiàn)各種插件。另一種是性能的可擴展性,系統(tǒng)的彈性擴容能力,即隨著系統(tǒng)用戶量、并發(fā)的增加是否可實現(xiàn)彈性擴容,通過增加硬件設(shè)備就能提供更強的處理能力,這種一般稱為可伸縮性??缮炜s性是高性能、低成本和可維護性等諸多因素的綜合考量和平衡,可伸縮性講究平滑線性的性能提升,更側(cè)重于系統(tǒng)的水平伸縮,通過廉價的服務(wù)器實現(xiàn)分布式計算。
10) 架構(gòu)的重用原則
重用是為了避免重復(fù)勞動、降低成本。架構(gòu)設(shè)計的過程中可將一些公共部分抽象提取形成公共類和接口,其它功能模塊所需相關(guān)功能可以調(diào)用,以達到重用目的?,F(xiàn)實中,已在架構(gòu)重用上做了很多的工作,譬如框架。使用功能分解的規(guī)則有助于提高重用性,因為每個類和方法的精度都提高了。
微服務(wù)架構(gòu)中,每個服務(wù)都必須實現(xiàn)許多跟基礎(chǔ)設(shè)施相關(guān)的功能,包括可觀測性和服務(wù)發(fā)現(xiàn)模式,還需實現(xiàn)外部化配置模式,以在運行時向服務(wù)提供數(shù)據(jù)存儲憑據(jù)等配置參數(shù)。在開發(fā)新服務(wù)時,一種更好的方法是在處理這些問題時應(yīng)用微服務(wù)基底模式,在現(xiàn)有成熟的基底框架之上構(gòu)建服務(wù)。
11) 設(shè)計系統(tǒng)運行時
現(xiàn)在的系統(tǒng)趨于復(fù)雜化、大型化, 構(gòu)架設(shè)計應(yīng)使系統(tǒng)可以預(yù)測系統(tǒng)故障,防患于未然。因此通過合理的構(gòu)架規(guī)劃系統(tǒng)運行資源,便于控制系統(tǒng)運行、監(jiān)視系統(tǒng)狀態(tài)、進行有效的錯誤處理;為實現(xiàn)上述目標,模塊間通信應(yīng)盡可能簡單,同時建立合理、詳盡的系統(tǒng)運行日志,通過自動審計運行日志了解系統(tǒng)運行狀態(tài)、進行有效的異常處理。
關(guān)注控制流、通訊機制、資源爭用、鎖/令牌環(huán)機制、同步異步、并發(fā)/串行,同時也要考慮質(zhì)量屬性。
基于系統(tǒng)運行時的質(zhì)量需求考慮問題,關(guān)注于系統(tǒng)的非功能需求。譬如客戶常常要求系統(tǒng)的功能畫面的最長響應(yīng)時間不超過4秒,能滿足2000個用戶同時在線使用,基于角色的系統(tǒng)資源的安全控制等等。
12) 監(jiān)控、能觀性
運維的一項重要工作就是搞明白應(yīng)用在運行時的一些行為,同時能夠根據(jù)錯誤的請求或者高延遲等故障進行診斷排錯。設(shè)計具備可觀測性的服務(wù):
健康檢查API:可以返回服務(wù)健康狀態(tài)的API;
日志聚合: 將產(chǎn)生的日志寫入集中式的日志服務(wù)器,提供日志搜索,也可據(jù)日志情況觸發(fā)報警;
分布式追蹤:為每一個外部請求分配一個唯一ID,用于在各個服務(wù)之間追蹤請求;
異常跟蹤:將程序異常發(fā)送到異常跟蹤服務(wù),給開發(fā)者發(fā)送告警并且跟蹤每一個異常的解決;
應(yīng)用指標:供維護使用的指標,如計數(shù)器等,導(dǎo)出到指標服務(wù)器;
審計日志:記錄用戶的行為。
13) 系統(tǒng)安全性設(shè)計
隨著應(yīng)用的不斷深入和擴大,涉及的場景和信息也越來越多,大量涉密信息在網(wǎng)絡(luò)上傳輸,因此對系統(tǒng)安全性的考慮已成為系統(tǒng)設(shè)計的關(guān)鍵,需從各個方面和角度加以考慮來保證數(shù)據(jù)資料的絕對安全。譬如在Web應(yīng)用中,面對各種安全風(fēng)險(SQL注入,XSS、CSR攻擊等),各種漏洞是否能堵住,架構(gòu)是否可以做到限流作用,防止DDOS攻擊等。在微服務(wù)架構(gòu)體系中,用戶身份驗證的工作通常由API網(wǎng)關(guān)/數(shù)據(jù)面來完成,服務(wù)調(diào)用方必須將有關(guān)用戶的信息(如身份和角色)傳遞給它調(diào)用的服務(wù),常見的解決方案是應(yīng)用訪問令牌模式,將訪問令牌(如JWT)傳遞給服務(wù),對令牌加以驗證并獲取有關(guān)用戶的信息。
參考博客:【云原生零可信網(wǎng)絡(luò)下-應(yīng)用服務(wù)的安全】
4. 設(shè)計視圖及架構(gòu)
架構(gòu)可從多視角來看,就像建筑架構(gòu),一般有結(jié)構(gòu)、管線、電氣等。4+1視圖是描述應(yīng)用程序架構(gòu)的絕佳方式,四個不同的軟件架構(gòu)視圖,每一視圖描述架構(gòu)的一個特定的重要側(cè)面,且包括一些特定的元素及它們相互之間的關(guān)系。
邏輯視圖:在面向?qū)ο蟮恼Z言中,這些元素是類和包。它們之間的關(guān)系是類和包之間的關(guān)系,包括繼承、關(guān)聯(lián)和依賴。
實現(xiàn)視圖:構(gòu)建編譯系統(tǒng)的輸出,由表示打包代碼的模塊和組件組成。它們之間的關(guān)系包括模塊之間的依賴關(guān)系以及組件和模塊之間的組合關(guān)系。
進程視圖:運行時的組件,每個元素都是一個進程,進程之間的關(guān)系代表進程間通信。
部署視圖:進程如何映射到機器,視圖中的元素由(物理或虛擬)計算機和容器進程組成,機器之間的關(guān)系代表網(wǎng)絡(luò)。該視圖還描述了進程和機器之間的關(guān)系。
視圖中的+1是指場景,負責(zé)把把視圖中的元素如何協(xié)作串聯(lián)在一起。每個場景負責(zé)描述在一個視圖中的多個架構(gòu)元素如何協(xié)作以完成一個請求。例如,在邏輯視圖中的場景展現(xiàn)了類是如何協(xié)作,在進程視圖中的場景展現(xiàn)了進程的協(xié)作。
系統(tǒng)的物理架構(gòu)
主要考慮硬件選擇和拓撲結(jié)構(gòu),軟件到硬件的映射,軟硬件的相互影響。
5. 持續(xù)交付 & 部署
實現(xiàn)項目/系統(tǒng)的持續(xù)交付和持續(xù)部署是DevOps中的關(guān)鍵環(huán)節(jié)。持續(xù)交付能夠以可持續(xù)的方式安全、快速地將所有類型的更改(功能、配置、錯誤修復(fù)和實驗等)交付到生產(chǎn)環(huán)境或用戶手中,其關(guān)鍵特征是軟件總是隨時可以交付,它依賴于高水平的自動化,包括自動化測試。持續(xù)部署把持續(xù)交付提升到了一個新的水準,實施持續(xù)部署的高績效組織每天多次部署到生產(chǎn)環(huán)境中,生產(chǎn)中斷的次數(shù)要少得多,且可以從發(fā)生的任何事情中快速恢復(fù)。
微服務(wù)架構(gòu)天然支持持續(xù)交付和持續(xù)部署。
持續(xù)交付和部署的目標是快速可靠地交付軟件,評估的四個有用指標如下:
部署頻率: 軟件部署到生產(chǎn)環(huán)境中的頻率;
交付時間: 從開發(fā)人員提交變更,到變更被部署的時間;
平均恢復(fù)時間:從生產(chǎn)環(huán)境問題中恢復(fù)的時間;
變更失敗率: 導(dǎo)致生產(chǎn)環(huán)境問題的變更提交百分比。
系統(tǒng)要小構(gòu)建、小發(fā)布、快試錯、不斷迭代與交付。傳統(tǒng)組織中部署頻率低,交付時間很長,特別是開發(fā)和運維人員通常都會在維護窗口期間熬到最后一刻。相比之下,DevOps組織經(jīng)常發(fā)布,通常每天多次發(fā)布,生產(chǎn)環(huán)境問題要少得多。如,亞馬遜可做到秒級就將代碼更改部署到生產(chǎn)環(huán)境,Netflix的一個軟件組件的交付時間為分鐘級。
持續(xù)交付和部署縮短了產(chǎn)品的上市時間,使企業(yè)能快速響應(yīng)客戶反饋,提供其所期望的可靠服務(wù)。開發(fā)人員可因此花費更多時間來提供有價值功能,而不是四處擔(dān)任救火隊員。
三. 軟件架構(gòu)的靈活設(shè)計
軟件組件模塊之間松耦合常見有兩種方式:接口和消息,這兩者如同人的骨關(guān)節(jié)一樣,連接著松耦合的雙方。消息比接口更加松耦合,因為接口方法有可能改變,導(dǎo)致接口的使用者跟著變化,而通過消息,消息生產(chǎn)者只要發(fā)送消息到消息系統(tǒng)就可以了,不再管是誰來接受消費這個消息,消息生產(chǎn)者由此和消費者完全解耦了。設(shè)計示例:
復(fù)雜性
假設(shè)原來是只有兩個組件進行交互:
需求不斷擴展變化,增加了第三個組件,那么這三個組件之間任意交互,復(fù)雜度就會提高:
復(fù)雜度以指數(shù)級的增長是驚人的,當(dāng)我們增加到六個組件,復(fù)雜度將是驚人的。
自然界是如何應(yīng)對這復(fù)雜呢?在物理中被稱為構(gòu)造定律。構(gòu)造定律致力于描述能量和物質(zhì)在物理網(wǎng)絡(luò)(如河流)和生物網(wǎng)絡(luò)(如血管)中的流動,理論提出,如果一個流體系統(tǒng)要繼續(xù)存在(比如,生存),那它必須始終提供更容易的方式來獲得這個系統(tǒng)中的流體。換句話說,系統(tǒng)應(yīng)該致力于將能量消耗減少到最低限度,而同時將消耗單位能量產(chǎn)生的熵提高到最大限度。進化實質(zhì)上是這么一個過程,即生物體不斷的重組他們自身,以使能量和物質(zhì)能夠盡可能迅速高效的通過他們。更好的流體結(jié)構(gòu),不管它們是動物還是河流,將取代那些較差的結(jié)構(gòu)。
如何設(shè)計出一種結(jié)構(gòu)以促成流經(jīng)這個結(jié)構(gòu)的用戶請求能更有效地獲得響應(yīng)呢?很顯然,多個組件發(fā)生任意關(guān)聯(lián)的方式肯定是不經(jīng)濟的,熵值副作用很大,如果變成以下這種結(jié)構(gòu),組件能夠?qū)崿F(xiàn)分組,三個元素是一組,另外一組是四個元素,組與組之間通過一個代表元素關(guān)聯(lián):
單一職責(zé)功能是關(guān)鍵,意味著只做一件事。它與讓事情DRY原則是一致的:每一個知識都必須有一個單一的、明確的、在一個系統(tǒng)內(nèi)的權(quán)威明確表示。不要重復(fù),需要干脆。程序中的每一個重要的功能都應(yīng)該在源代碼中的一個地方實現(xiàn),將業(yè)務(wù)規(guī)則、長表達式,if語句、數(shù)學(xué)公式和元數(shù)據(jù)等各自放在一個地方。單一職責(zé)也與委托原則有關(guān),只有放棄一些才能獲得一些,有舍有得,放棄的就是委托其他類實現(xiàn):不要自己做所有的事情,可以委托給相應(yīng)的類去完成。因為每個組件都秉持單一職責(zé),組件之間才可能發(fā)生唯一的一個關(guān)聯(lián)關(guān)系。
高凝聚意味著模塊的復(fù)雜性降低。因為變得有條理,復(fù)雜性自然降低。組件之間的強關(guān)系用耦合表達,保留下好的強關(guān)系也就是高凝聚,去除不好的強關(guān)系也就是低耦合。高凝聚、低耦合是降低復(fù)雜性提高靈活性設(shè)計中重要的宗旨。
除了靜態(tài)的結(jié)構(gòu)關(guān)系以外,高凝聚、低耦合還表達為對象的方法行為上,這需要通過分配職責(zé)來實現(xiàn)。什么是職責(zé)?它包括三個方面:
1)? ?對象應(yīng)該執(zhí)行的動作;
2)? ?對象包含的知識如:算法 約束 規(guī)格 描述;
3)? ?當(dāng)前對象影響其他對象的主要因素。
以報童向買報人收錢為例:報童應(yīng)該向顧客收取兩塊錢的買報費,他是直接把顧客的錢包拿過來從中掏出兩塊錢,還是請求顧客自己從錢包里掏出兩塊錢呢?無疑是后者。
這兩者的區(qū)別是什么?Tell, Don't Ask原則:
報童只要告訴顧客做什么,而不直接參與怎么做(你的錢夠嗎?夠才能買)。報童只給顧客一個命令,而不必關(guān)注顧客是如何執(zhí)行這個命令。Tell, Don't Ask原則能夠讓我們保證兩個組件之間在動作上不會發(fā)生過于細節(jié)過多的耦合,而只是通過一個消息就能完成,通過發(fā)送消息告訴對方我需要什么,而不是直接把對方拎過來搜身。至此,完成了兩個組件之間最大松耦合,實現(xiàn)了架構(gòu)設(shè)計的最大可能的靈活性。
軟件架構(gòu)復(fù)雜性必然如自然界任何事物一樣不斷發(fā)展,作為軟件架構(gòu)師如何學(xué)習(xí)大自然的巧妙設(shè)計,如同庖丁解牛一樣切分復(fù)雜性為單一職責(zé),再從結(jié)構(gòu)和行為的高凝聚關(guān)系方面進行組合或消息傳遞,從而真正實現(xiàn)高凝聚、低耦合的靈活性目標。
---------未完,請看(下)-----------
萬字長文探討可信構(gòu)架之道(下)
軟件開發(fā) 架構(gòu)設(shè)計 運維
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。