萬字長文探討可信構架之道(下)
四. 典型架構對比分析
系統架構跟隨技術的發展不斷升級和改進,從傳統的單一架構演變為分布式、微服務架構、Serverless架構,以下列舉了主要的四種軟件架構以及它們的優缺點。
1. 單體應用架構
單體架構的應用開發簡單,易于更改,測試、部署簡單及便于橫向擴展。但隨著需求不增加,管理成本也不斷提高,代碼庫飛速地膨脹。慢慢地單體應用變得越發臃腫、復雜、不可靠,可維護性、靈活性逐漸降低,維護成本越來越高。單體程序陷入單體地獄,開發變得緩慢和痛苦,敏捷開發和部署已經不可能。應用變更后,開發團隊將其更改提交到單個源代碼倉庫,從代碼提交到生產環境的路徑漫長而艱巨。
問題點:
過度復雜:以一個百萬行級別的單體應用為例,整個項目包含的模塊非常多、邊界模糊、依賴關系不清晰、代碼質量參差不齊、混亂地堆砌在一起,可想而知整個項目非常復雜。每一次更改都會讓代碼庫變得更復雜、更難懂。應用一步一步地成為一個巨大的、令人費解的“臟泥球”。
開發速度緩慢:IDE工具使用變慢,構建一次應用需要很長時間,應用太大導致每啟動一次都需要很長時間。從編輯到構建、運行再到測試這個周期花費時間越來越長,嚴重影響團隊的工作效率。
部署頻率低:代碼增多,構建和部署的時間也會增加。每次功能的變更或缺陷修復都會導致需重新部署,全量部署耗時長、影響范圍大、風險高,使得應用上線部署的頻率較低,出錯率較高。
擴展能力受限:單體應用只能作為一個整體進行擴展,無法根據業務模塊的需要進行伸縮。
可靠性差:程序體積龐大而無法進行全面和徹底的測試,意味代碼中的錯誤進入生產概率大。所有模塊都在同一進程中運行而缺乏故障隔離,一個模塊中的錯誤將導致所有實例崩潰。
阻礙技術創新:體現在團隊必須長期使用一套相同技術棧,采用新的框架和編程語言變得極困難。采用或嘗試新技術極其昂貴和高風險,因為應用必須被徹底重寫。
2. 分布式體系架構
隨著業務的深入,要求的產品功能越來越多,業務模塊邏輯也變得更加復雜,使得單體應用變得越發臃腫,可維護性、靈活性降低,開發周期越來越長,維護成本越來越高。此時需要對系統按照業務功能模塊拆分演變成一個分布式系統,將業務模塊部署在不同服務器上,模塊之間通過接口進行數據交互,通過負載均衡結構提升系統負載能力。
架構特點介紹
降低耦合:把模塊拆分,使用接口通信, 降低了模塊之間的耦合度;
責任清晰:把項目拆分成若干個子項目,不同的團隊負責不同的子項目;
擴展方便:增加功能時,只需要再增加一個子項目,調用其他系統的接口即可;
部署方便:可以靈活的進行分布式部署;
提升代碼復用:采用分布式服務方式構建公用服務層,減輕開發量。
缺點:系統之間交互使用遠程通信,接口開發工作量增大。
3. 微服務化架構
參考博客:【微服務架構及ServiceMesh技術框架介紹】
由松耦合和具有邊界上下文的元素組成,是將應用程序功能性分解為一組服務的架構風格。每個服務都是由一組專注的、內聚的功能職責組成,使用服務作為模塊化的單元。服務的API為它自身構筑了一個不可逾越的邊界,無法越過API去訪問服務內部類。可為應用程序提供更高的可維護性、可測試性和可部署性,同時提升了開發效率、應用可擴展性等。服務的本質是圍繞業務而非技術問題進行組織的,反應業務語義、自包含、無狀態、跨邊界。
定義微服務架構
使用更抽象的系統操作概念將應用的需求提煉為各種關鍵請求,系統操作是描述服務之間協作方式的架構場景;然后確定如何分解服務,有幾種策略, 一種是定義與業務能力相對應的服務, 另一策略是圍繞領域驅動設計的子域來分解和設計服務,每一服務都有它自己的領域模型。策略圍繞業務而非技術概念分解和設計服務,策略的結果都是一樣的:一個包含若干服務的架構,是以業務而不是技術概念為中心;最后是確定每個服務的API。
將標識的每個系統操作分配給服務,服務可獨立地實現操作或可能需與其他服務協作。服務的分解需考慮網絡延遲, 自包含,跨系統邊界的數據一致性等,使用領域驅動設計中的概念來消除所謂上帝類。每一個系統操作的行為都通過領域模型的方式來描述,每一個重要的系統操作都對應著架構層面的一個重大場景,是架構中需要詳細描述和特別考慮的地方。
進行微服務拆分
領域驅動設計是構建復雜軟件的方法論,這些軟件通常以面向對象和領域模型為核心。領域模型以解決具體問題的方式包含了一個領域內的知識。它定義了當前領域相關團隊的詞匯表,DDD也稱之為通用語言。領域模型會被緊密地映射到應用的設計和實現環節。在微服務架構的設計層面,DDD有兩個特別重要的概念,子域和限界上下文。
傳統企業架構建模方式往往會為整個企業建立一個單獨的模型,會有適用于整個應用全局的業務實體定義,例如客戶或訂單。這類建模方式會帶來譬如一致性、復雜性及不同團隊使用上的混亂等問題。DDD采取完全不同的方式,定義多個領域模型來避免,每個領域模型都有明確范圍,領域驅動為每一個子域定義單獨的領域模型。子域是領域的一部分,領域用來描述應用程序問題域的一個術語,識別子域的方式跟識別業務能力一樣。DDD把領域模型的邊界稱為限界上下文,限界上下文包括實現這個模型的代碼集合。在微服務架構中,每一個限界上下文對應一個或者一組服務。每一個子域都有屬于它們自己的領域模型。
微服務架構好處
1) 使大型應用可持續交付和部署
持續交付和持續部署是DevOps的一部分,DevOps是一套快速、頻繁、可靠的軟件交付實踐,高效能的DevOps組織通常在將軟件部署到生產環境時面臨更少的問題和故障。微服務架構通過以下三種方式實現持續交付和持續部署:
擁有CI和CD所需的可測試性: 自動化測試是持續交付和持續部署的一個重要環節。每一個服務都相對較小,編寫和執行自動化測試變得很容易,應用程序的bug也就更少。
擁有CI和CD所需的可部署性: 每個服務可獨立于其他服務進行部署。如負責服務的人員需要部署對該服務的更改,無需與其他人員協調就可進行。因此,將更改頻繁部署到生產中要容易得多。
實現團隊的自治(自主且松耦合): 將組織構建為一個小型團隊集合。每個團隊負責一個或多個服務的開發和部署, 可獨立于所有其他團隊開發、部署和擴展他們的服務,提升了開發效率。
2) 服務較小且易維護
每個服務都較小,開發者更易理解,較小規模的代碼庫可提升開發者的工作效率,而整個應用是由若干微服務構建而成,所以也會被維持在一個可控狀態。快速啟動的服務能提高效率,加速研發過程。
3) 服務可獨立擴展
支持不論是采用X軸擴展的實例克隆,還是Z軸擴展的流量分區方式。每個服務都可部署在適合環境。
4) 更好的容錯性
可實現更好的故障隔離。如某個服務中的內存泄漏不會影響其他服務,其他服務仍可正常地響應請求。
5) 技術棧不受限
可結合項目業務及團隊特點合理選擇技術棧。這跟單體架構是完全不同,單體架構之下的技術選型會嚴重限制后期新技術的嘗試。
微服務架構弊端
服務拆分和定義的挑戰:服務的拆分和定義更像是一門藝術。如果對系統的服務拆分出現了偏差,很有可能會構建出一個分布式的單體應用:一個包含了一大堆互相之間緊耦合的服務,卻又必須部署在一起的所謂分布式系統。將會把單體架構和微服務架構兩者的弊端集于一身。
分布式固有復雜性:系統容錯、網絡延遲、數據一致性、分布式事務等都會帶來較大挑戰,服務必須使用進程間通信機制。此外,必須設計服務來處理局部和遠程服務不可用/高延遲的各種情況。
運維要求較高:單體架構中只需保證一個應用的正常運行,而在微服務中需要幾十甚至幾百個服務的正常運行與協作,給運維帶來了挑戰。要成功部署微服務,需高度自動化的基礎設施。
協調更多開發團隊:當部署跨越多個服務的功能時需要謹慎地協調更多開發團隊,必須制定一個發布計劃,把服務按照依賴關系進行排序。這跟單體架構下批量部署多個組件的方式截然不同。
接口調整成本高:服務間通過接口通信。如修改某一微服務,可能使用了該接口的微服務都需調整。
重復性工作可能:很多服務可能會使用到相同功能,而該功能并未達到分解為一個微服務程度,可能會導致對這一功能的重復開發,盡管可使用共享庫來解決但需面對多語言環境問題。
微服務架構已成為任何依賴于軟件技術的企業業務的重要基石,是一把好處和弊端共存的雙刃劍。在使用微服務架構時,一些問題無法回避,每個問題都可能存在多種解決辦法,同時伴隨著各種權衡和取舍,并沒有一個完美的解決方案。
4. Serverless架構
低運營成本,簡化設備運維,提升可維護性,更快的開發速度等。
參考博客:【云計算的可信下半場-無服務器 Serverless】
五. 服務化構架之可信談
微服務架構是一種架構模式,它提倡將單一應用程序劃分成一組小的服務,服務之間相互協調、互相配合,為用戶提供最終價值。每個服務運行在其獨立的進程中,服務和服務之間采用輕量級的通信機制相互溝通。每個服務都圍繞著具體的業務進行構建,架構上可為應用程序提供更好的可維護性、可測試性和可部署性,以及快速迭代和交付能力等。
說明:對本章節的理解需要一定的微服務化架構基礎知識。
參考博客:【微服務架構及ServiceMesh技術框架介紹】【ServiceMesh-服務注冊發現的應用適配】
【ServiceMesh-Istio流量速率限制介紹】 【云原生零可信網絡下 - 應用服務的安全】
1.? 注冊中心與配置中心
很多人經常把注冊中心和配置中心混為一談,有這種觀點的認為服務注冊數據其實就是配置的一種,這樣解釋也不無道理,的確注冊中心的數據是配置的一種。但注冊中心之所以獨立存在,那是因為注冊中心的數據有一定的業務獨立性,是為了描述微服務相關。注冊中心完全不依賴配置中心,而是一個獨立的、高可用、數據一致的系統。設計意識形態上要清晰注冊中心和配置中心的用途實質,區分是服務的注冊/發現,還是配置的登記與更新、通知。
注冊/反注冊:保存服務提供者和服務調用者的信息;
訂閱/取消訂閱:服務調用者訂閱服務提供者的信息,支持實時推送。
2.? 業務與技術面的解耦
模塊間交互越多,其耦合性越強,同時表明其獨立性越差。耦合性是對模塊間關聯程度的度量,是指模塊之間的依賴關系,包括控制關系、調用關系、數據傳遞關系。耦合的強弱取決于模塊間接口的復雜性、調用方式以及通過界面傳送數據的多少。
需要將業務和服務治理中間件能力分離,否則異構的業務必然帶來服務治理能力的非標準化。應用與技術面的解耦可帶來應用實現的語言無關性(Java/Go/Python/C++等), 應用升級維護的無感知,以及模塊的擴展性、穩定性等。
應用與治理分離,業務應用和治理能力的就近物理切割。通過部署本地代理的方式來完成這個切割。
強調執行和控制的分離,也即控制平面和數據平面的切分。實現對業務進程的零/少侵入原則,將服務治理能力看待為協議棧的一部分。
實踐準則:
業務應用側與服務系統平臺內的各個模塊的關系應該保持松耦合。
避免業務應用側與數據面通訊模塊的耦合,與通訊相關的功能應交由數據面通訊模塊負責。在服務交互層面上,保持業務應用的輕形態,深入貫徹好信息專家模式。
避免業務應用側的服務交互策略處理模式與注冊中心分離。
避免業務應用側進程內狀態事件監測與傳播的能量消耗的放大。
業務應用系統的敏捷化催生了開發的語言、技術實現的無關性,保持接口的兼容性。
……
3.? 服務化的數據模型
系統關鍵數據模型,數據對象的生命周期等在設計之初就需明確和具有較為清晰的定義。數據模型是系統架構設計的重要組成部分,對模型對象的定義和使用,反向亦可折射出系統的架構設計的優劣。
服務對象模型是對服務的自描述,包含服務基礎信息,環境信息、服務能力及Qos等。一個服務對象代表了一個服務整體,盡可能保持其無狀態、自包含特征。對于有狀態的服務可以建立數據存儲(內存/SSD磁盤)關聯。
服務對象模型為業務相關性,對于基礎技術的能力也可發布成為一類服務,保持數據對象模型的一致,兩者的區別在對象屬性和操作特征上。
服務對象模型要保持原子性,如果硬性拆分成多個子對象且在系統內的不同場景使用,譬如拆分成:服務基礎對象、服務交互對象、業務領域對象,控制策略對象等各個模型。必然給分布式系統的整體帶來復雜性、穩定性問題。也使得模塊間的耦合程度加大。
4.? 服務的注冊與注銷
服務的本質反映業務語義,可包括多個方法,具有自包含特征。如訂單服務包含有查詢訂單、新增訂單等。每個服務實例需向注冊中心來注冊和解除注冊,而注冊中心主要起協調者的作用,可借助注冊中心來發現已登記并可使用的服務。
服務的注冊應該是極簡模式, 意指服務實例只需完成注冊、維持保活即可。需要對外提供服務的接口需要注冊,進程內交互的無需對外發布。
業務應用側將系統內的服務成功注冊到注冊中心,即代表了服務的生命周期的開始,宣告了業務應用側的微服務接口的可用但并非可達。邊車模式下還需等待進行服務發現。
對于服務使用方來說,需聲明其需要的服務,及對其自身在該服務上的使用方式進行定義或默認策略。對于服務提供方,則需要定義服務的基礎信息和能力模型(協議、并發、編解碼,信息大小等等),消費方的能力受限于服務提供方的能力輸出。
基于業務領域服務和服務的交互形態,要明確哪些服務需要注冊,是否反映了業務語義。登記注冊在注冊中心的信息必定是需要發現的關鍵資源,如果是來自業務應用側的技術性的登記注冊,應該從對象模型形態上構建關聯,作為服務的屬性(靜態、動態)。
注冊中心屬于關鍵核心基礎件,保障注冊中心的穩定、可靠、高性能不僅需要其自身的優化,也需要關聯方的最佳使用方式,在最合理的方式上來產生交互和信息傳遞。
業務應用與注冊中心的直接交互為最佳實踐,其它則會以犧牲簡易、擴展、穩定為代價。
5.? 服務的平臺發現模式
服務消費方與服務提供方具有動態特征,需要登記到注冊中心,包括兩者的地址等環境信息。
客戶側服務發現模式,業務應用側既要進行服務的注冊,也要承擔服務發現的職責。在分布式體系的生產集群環境,所有的業務進程都會存在與注冊中心的Watch訂閱,獲取注冊中心的服務關聯方信息的推送。缺陷為訂閱/推送的點過多,注冊中心也需要為此產生一定的資源消耗,關聯的點越多,消耗越大。
在通訊邊車模式下,服務交互、路由等需經過數據面來處理,可由控制面適配層來提供服務發現,服務端和客戶端不必承擔服務發現職責。相比客戶側發現模式,訂閱及信息推送的量只有原來的 1/N,大幅、有效減輕了注冊中心壓力。(服務對象模型要保持唯一性,避免注冊信息與通訊模型對象的分離),同時,也進一步解耦業務應用側與其它模塊。
對于需要從注冊中心獲取登記的基礎服務(譬如存儲服務), 可結合查詢方式和保底原則,減少不必要的向注冊中心的訂閱與事件觸發后的信息推送。
6.? 控制面適配層的職責
控制面適配層的職責在于對接通訊邊車模塊,實現控制指令下發、路由、負載、流控、熔斷、安全等與服務治理相關的策略的管理。
?與數據控制面適配層交互的模塊包括:注冊中心,各POD內的通訊邊車模塊進程,狀態監控模塊,管控(控制面)模塊。與注冊中心交互進行服務發現及實現與服務生命周期相關的操作;從管理控制面獲取管控策略及安全策略;從狀態監控模塊獲取業務應用側事件狀態并聯動業務側邊車模塊。
控制面適配層模塊,業務應用側程序兩者應無實質性直接交互,要實現解耦。如產生耦合則會增加系統的復雜度。解耦方式可通過借助API接口來實施或委托隔離。
訂閱全局服務信息并進行集中控制,需確保控制面適配層的可靠、穩定性,無單點問題。
對于策略、控制信息等需要確保可靠下發。
7.? 服務調用與數據通訊
業務應用程序將業務領域服務,包括服務基礎信息、實例環境、服務能力(Qos等)向注冊中心登記,即開啟了服務的生命周期。通訊的方式承載了服務與服務之間的交互,好的構架應該將數據面通訊模塊的交互關系降到最低。
服務注冊完畢即對外宣告了其微服務組件的聲明式API。在邊車模式的體系架構下,服務的調用需要數據面提供的信息路由,將請求提交給服務提供方來處理。
服務的交互是面向接口的調用,實例名信息、類別(請求方|提供方等)、環境信息等用于跨進程的尋址,而接口方法名用于進程空間內的回調,名稱的可理解性、辨識性要好于數字標識。簡單原則,能只依賴靜態信息的絕不使用動態特征,減少系統的可變性依賴、耦合,特別是分布式系統場景,要減少子系統與子系統,模塊與模塊之間的耦合依賴。
通訊鏈路的設計原則,應該致力于將能量消耗減少到最低限度,將鏈接精準建立在必須的場景。避免鏈路的密集交互,大量的文件句柄,取代那些較差的結構。提升系統的整體穩定性,以及減少了分布式系統整體的資源消耗,包括計算、內存及帶來的過多消耗導致的垃圾回收異常問題等。
數據通訊模塊要確保高內聚、低耦合,主要職責在服務信息路由、負載均衡調度等服務治理相關。確保職責單一,要與業務應用側盡可能的解耦。如果業務應用側兼有路由控制則導致通訊控制職責分散,給系統帶來復雜性,同時不利系統橫縱擴展、不利業務實現的敏捷、不利高效交付等。也要避免引入與注冊中心、狀態監控模塊的強關聯。
外部系統可通過在服務注冊中心獲取服務的基礎信息,或面向契約式地址的交互。通過數據面的路由抵達業務應用側,可集中實施安全、審計、負載、流控等控制操作。
服務交互,通訊模式需要依據場景來提供同步、異步,請求的單向與雙向等,在協議上可以考慮 HTTP, gRPC, REST,TCP/IP等的最佳適配,協議的使用做到可插拔。
通訊類型、模式、形態等盡可能保持一致,簡化對外的操作形態,特別針對業務應用側。提升系統對外接口操作的透明度,將處理細節封裝在接口內部。
鏈路的可用性檢測,隔離、熔斷等服務治理操作職責盡可能限定在數據面的邊車模塊。
. . . . . .
8.? 服務的治理、策略
服務治理是個非常大的話題,真的要鋪開來講,幾篇文章的篇幅都談不完。這里先簡單地看一下服務治理要解決的問題。微服務化通過將復雜系統切分為若干個微服務來分解和降低復雜度,使得這些微服務易于被使用和維護。微服務的連接、服務注冊|發現、路由、負載均衡、服務熔斷、隔離,服務限流、降級,訪問控制(認證、鑒權)、監控(日志、鏈路追蹤、預警等)、AB測試,金絲雀發布等等,這些即是服務治理的內容。
在這里想說的是,每項服務治理的內容,都需要進行充分設計。需要清晰定義治理的目標,數據模型,治理策略的下發,治理實施的最佳控制點(確保職責單一)及聯動模塊,最佳的控制流、事件信息流,治理的關鍵路徑(降低耦合),治理期間的周期性活動,治理的邊界,治理對各個模塊的侵入最小化,治理對資源的消耗,治理的總體可控,接口使用透明等等。治理是服務的治理,從數據模型上盡可能與服務對象模型融合。
服務流控:流控的能力必須要有但要慎用。流控要體現在服務面上(某種程度上反映了服務提供方的能力限定),策略模型與服務領域模型應該是依附關系,策略(令牌、速率、并發度等)及管控指令的下發應該通過數據控制面適配層來推送;流控的最佳集中控制點在數據面的通訊邊車模塊(可感知提供方的受壓-主動/被動,可反壓傳導至服務請求方);減少業務應用側的耦合,應該通過接口形態反壓傳導到業務應用程序并聯動控制;可以支持直接業務應用側的流控手段,但前提是與平臺內基礎模塊的解耦。
通過加入埋點、數據監控,能及時發現系統出現的問題,及時預警,及時介入處理。
鏈路檢測、跟蹤應以端到端的形態體現,需要解耦應用側與通訊數據面。
日志處理以Node為匯聚點(Pod, Container,進程)統一歸并后流式歸集,實時分析。
以多點,多維度,多形式進行系統平臺內的指標監控、收集,健康檢查等。
監控系統(狀態、事件),其它(存儲服務):解除節點內與節點外監控點的關聯耦合。
. . . . . .
9.? 高可用狀態監測
監測分布式系統內的各個子系統,模塊,組件的狀態,用以異常控制、預警聯動,故障隔離與遷移恢復等等。
單一Node內的狀態檢測由Node、容器級別的監測代理集中采集上報,同時避免狀態信息在Node級代理間傳播,基于結構化本質,也無需構建Node級的全互聯鏈路。如此,加強了事件監測狀態在Node內的內聚和Node間的松耦合,極大減少資源消耗。
Node監測的事件狀態上送到平臺內狀態歸集中心集群節點(N+1), 并作異常事件檢測和規則觸發后的預警。對于異常事件狀態,以及觸發的點、面需綜合權衡設計,將關聯影響方降到最低以最小化事件擾動,原則應交由對異常事件具有絕對控制職責的模塊且在關鍵點來決策聯動。以致力于將能量消耗減少到最低限度,同時將消耗單位能量產生的熵提高到最大限度。
為區分事件的來源于Node、Container、進程,可引用事件狀態機與即時探測模式。
為進一步解耦應用與技術面,通訊路由、信息傳輸應集中在剝離的通訊子系統解決。
系統運行時關注在異常事件及處理,要規劃好分布式架構內的操作控制流、數據流及其關鍵路徑,避免把正常事件當作常態化處理模式,避免數據的全量廣播,避免系統的設計過度、冗余化可能,避免給整體架構帶來復雜性,以至降低了系統穩定性、可靠性。
. . . . . .
10. 服務發布版本與灰度
服務相對較小,以服務為單元的測試、發布、編排及部署、升級會變得更容易、可靠。每個服務可獨立于其它服務進行部署,因此,將更改頻繁部署到生產中要容易得多。確保服務的領域數據模型的唯一性,基于版本的控制可強化服務以業務為語義及利于其生命周期管理。服務發布,遷移控制等所涉及的數據對象模型需體現在服務對象模型上。
快速可靠地迭代與交付軟件,提升交付的敏捷以及故障恢復效率。傳統組織中部署頻率低,交付的時間很長。相比之下,DevOps組織經常發布軟件,生產環境問題要少得多。
灰度發布,服務實例遷移盡可能實現自治,減少對其它模塊的過多耦合依賴。
減少操作控制、目標狀態的不必要的傳播、交互的環節,貫徹簡單至上原則。
降低控制流,策略傳遞,信息傳遞,事件通知等等的用力面、傳播面,設定最佳處理點。
交互操作的用語盡可能與服務相關用語具有區分性。
11. 資源高利用率、低使用率
自身依賴組件/模塊是否與系統自身集成部分重復,應以復用優先、資源利用最大化為原則。
平臺內、模塊間的操作控制、事件/狀態觸發、數據傳輸,接口調用等,交互環節簡單化。
給予分布式系統內關鍵/核心模塊、子系統充足的資源,避免成為全局熱點。
資源的低使用率要從頂層架構上來設計,消除無用模塊、組件或通過歸并方式達到資源分配的最小化、利用率最大化。
分布式系統內運行時的資源消耗,要規劃、建立運行時模型,涉及控制、數據、資源等等。
避免分布式集群環境內的全網/區域全交互的訂閱,數據推送,事件廣播模式等。
… …
六. 架構設計衡量與愿景
好的軟件架構設計是產品質量的保證,特別是對于客戶常常提出的非功能性需求的滿足,不好的架構是對資源的浪費(人力、物力等)。成功的軟件架構設計必須遵循一定的原則、模式,失敗的軟件架構設計總是由于一些不確定的因素導致。
如果一個軟件開發程度在70%以上的情況下,加入一個新功能還需涉及到大量的文件、代碼的修改,那這個軟件架構一定很爛,而好的架構此時應該已經完成大部分底層組件的開發且相互獨立,加入的大部分新功能基本上是原有組件的功能組合(不涉及內部的修改),以及加入新功能特有的獨立組件。
如果每加入一個功能就有大量的修改提交,那么這個架構的質量,你懂的!
1.? 架構設計的衡量
架構為業務服務,沒有最優的架構,只有最合適的架構。架構始終以高效、穩定、安全等為目標來衡量其合理性。
業務需求的角度
?? ? ? ?能解決當下業務需求和問題;
?? ? ? ?高效完成業務需求,能以優雅且可復用的方式解決當下所有業務問題;
?? ? ? ?前瞻性設計,在未來一段時間都能以高效方式滿足業務,從而不會每次當業務進行演變時,導致架構翻天覆地的變化。
非業務需求角度
?? ? ? ?高可用:盡可能提高軟件可用性,通過黑白盒測試、單元、自動化、故障注入測試、提高測試覆蓋率等來一步一步推進;
?? ? ? ?文檔化:整個生命周期內都需做好文檔化,變動來源包括但不限于BUG,需求等;
?? ? ? ?可擴展:軟件的設計秉承低耦合的理念去做,在合理的地方抽象。方便功能更改、新增和運用技術的迭代,并且支持在適時對架構做出重構;
?? ? ? ?高復用:為了避免重復勞動,降低成本,希望能夠復用之前的代碼、設計。這點對于架構環境的依賴是較大的;
?? ? ? ?安全:組織運作過程中產生的數據都是具有商業價值的,保證數據的安全也是刻不容緩的一部分,以免出現XX門之類丑聞。加密、HTTPS等為普遍手段。
軟件架構應該是擁抱變化,性能穩定,易于維護。
?? ? ? ?可伸縮性: 服務是可擴展的,并且擴展的成本是比較合理的。當服務的負載增長時,系統能被擴展來滿足需求,且不降低服務質量;
?? ? ? ?高可用性: 盡管部分硬件和軟件會發生故障,整個系統的服務必須是7 *24小時可用,可通過軟件和硬件的冗余來實現;
?? ? ? ?可管理性: 整個系統可能在物理上很大,但應該容易管理,需要開發對應的管理工具;
?? ? ? ?價格有效: 架構設計需要考慮ROI因素,整個系統的實現是經濟的、易支付的。如果一個架構很好,但成本高得驚人,那不一定是合適的架構。
對系統架構優劣的評價參考
1)? ?系統性能
2)? ?可靠性(容錯/健壯性)
3)? ?可用性
4)? ?安全性
5)? ?可修改性(可維護、可擴展性,結構重組,可移植性)
6)? ?功能性
7)? ?可交互性
2.? 逆向的康威定律
對于大型和復雜的應用程序,微服務架構往往是最佳選擇。然而,除了擁有正確的架構之外,成功的軟件開發還需要在組織、開發和交付流程方面做一些工作。下圖展示了架構、流程和組織之間的關系:
為了能在使用微服務架構時能有效地交付軟件,需要考慮康威定律。組織架構和系統架構之間有一種隱含的映射關系,設計系統的組織其產生的設計等價于組織間的溝通結構。因此反向應用康威定律并設計你的企業組織,使其結構與微服務的架構一一對應。這樣可確保開發團隊與服務一樣松耦合。
若干小團隊的效率顯然要高于一個單一大團隊。微服務架構使得團隊可以實現某種程度的“自治”。每個團隊都可以開發、部署和運維擴展他們負責的服務,而不必與其他團隊協調。更進一步,當出現了某個服務故障或沒有滿足SLA等要求時,對應的責任人也非常清楚。而且開發組織的可擴展性更高,可以通過添加團隊來擴展組織。如果單個團隊變得太大,則將其拆分并關聯到各自負責的服務。由于團隊松散耦合,可以避免大型團隊的溝通開銷。因此,也可以在不影響工作效率的情況下添加人員。
3.? 架構設計的愿景
系統架構能描述軟件的整體且包括軟件的各個方面,但每一個設計細節總是需要單獨考慮,這時候就會出現設計細節之間、以及設計細節和架構之間的不一致。架構設計的各個部分之間的設計沖突是很容易發生的,發生的概率及頻率和團隊的規模成正比、和溝通的頻度及效果成反比。如不同模塊間的設計沖突導致了軟件無法正常運行時,我們就需要坐下來好好的審視,究竟發生了什么。
建立一個架構愿景,提供軟件全局視圖,包括所有重要部分,定義各個部分的責任和之間的關系以及設計需要滿足的原則。而這個愿景的設計源自需求,那些針對系統基本面的需求。比如說,系統特點是一個交互式還是一個分布式系統,這些需求將會影響到架構愿景的設計。同時,架構愿景也要滿足其它各種特點,譬如簡單、可擴展性、抽象性。簡單來說,把架構愿景當作一個mini的架構設計,由于是在單次迭代中討論架構愿景,因此從整體上考慮,架構愿景是也是在不斷的變化的。因為架構愿景代表了架構的設計,架構愿景的演進代表了架構設計的演進。
架構的愿景是相對于一個范圍來說的,在一個特定軟件功能范圍之內談架構愿景才有實際的意義,例如針對軟件的全局或某個子模塊。在這個特定的范圍中,訂立了架構愿景之后,這個范圍內的所有設計原則將不能違背架構愿景。這是非常重要的,是架構愿景的最大的用處。有了這樣的保證,就可以保證設計的一致性和有效性。任何一項設計的加入,都能夠融入到原先的架構中,使得軟件更加的完善,而不是更加的危險。
從整個開發周期來看,全局架構愿景是隨著迭代周期的進行不斷發展、修改、完善的。子模塊級、或是子問題級的架構愿景本質上和全局的愿景制定差不多,不能夠和全局愿景所違背
在操作上,全局愿景是設計團隊共同制定出來的,而子模塊級的架構愿景就可以分給設計子團隊來負責,而其審核則還是要設計團隊的共同參與。以確保各個子模塊間不至于相互沖突或出現空白地帶,且每個子設計團隊可從別人那里吸取設計經驗。一般來說,設計團隊取得了一致的意見就可確定全局架構愿景工作的基本完成。
子模塊(子問題)間的耦合問題。一般來說,各個子模塊間的耦合程度相對較小,而子問題間的耦合程度就比較大,如權限設計、財務等功能會被每個模塊使用。那么就需要為子模塊制定出合同接口,意指接口是正式的,不能隨意修改,會被其它設計團隊使用,如修改將會對其它的團隊產生無法預計的影響。合同接口的制定、修改都需要設計團隊的通過。此外,系統中的一些全局性的子問題最好提到全局愿景中考慮。
七. 構架系統的感與悟
1. 架構設計的常見誤區
?? ? ?架構專門由架構師來做,業務及開發人員無需關注;
?? ? 過早做出關鍵性決策;
?? ? ?架構設計企圖一步到位,世上沒有最好的架構,只有最合適的架構,不要企圖一步到位;
?? ? ?為虛無的未來埋單:某種程度上來說不要過多考慮未來的擴展,說不定功能做完后效果不好就無用了。如業務模式和應用場景邊界都已比較清晰,那應該適當的考慮未來的擴展性設計;
?? ? ?遺漏關鍵性約束與非功能需求;
?? ? ?高開高走落不到實處;
?? ? ?埋頭干活兒缺乏前瞻性;
?? ? ?為了技術而技術:技術是為業務而存在,除此毫無意義。在技術選型和架構設計中,脫離實際而一味追求新技術,可能會導致架構之路越走越難。成本、時間、人員等各方面都要綜合考慮;
?? ? ?架構設計無需考慮系統可測性。
2. 架構大方向必須正確
架構設計是軟件成敗的關鍵環節之一,決定了軟件的整體質量進而決定了客戶的滿意度。同時也決定了軟件的可擴展性、可維護性、穩定性以及性能等方方面面。架構大方向是概念架構設計,設計了正確的概念架構,表明軟件架構設計已經成功了一半。一個產品與其它同類產品在概念架構設計上的不同決定了軟件架構后續的發展導向。概念架構設計不關注具體的接口定義和實現細節,主要工作在于總體架構模式、技術選型等方面,是軟件架構設計輪廓性規劃和總體指導策略。
架構的方向性錯誤將導致后續設計、開發迭代的不穩定及復雜性,穩定性、擴展性等的缺失,基礎的重構也將導致資源的過多、不必要的消耗,這也是項目團隊所不愿意面對的。
3. 關于系統的重構參考原則
重構是基于已發現問題和潛在問題進行修正,也可說是一種還債行為,用更好的方式、方法來糾正以前代碼和設計中存在的問題,同時也是最大化減少產品發生問題的可能,讓系統減負運行。如代碼優化,剔除重復、違背代碼規范、模塊耦合問題等。可將整個系統分成很多個子模塊,以對各個子模塊各個擊破,最終完成對整個系統的重構,分而治之。
確保重構行為僅針對那些有重構需要的設計,需求的變更或對原設計進行改進以得到優秀簡潔的設計實現。對于一段凌亂的代碼,如不需要修改它就不需要重構。只有當你需要理解其工作原理時,重構才變得有價值,如果重寫比重構更加容易,那就無需重構。對于架構來說,可近乎等價的認為只是在外部接口不變的情況下對架構進行改進。而在實際的開發中,除非非常有經驗,否則在軟件開發全過程中保持所有的軟件接口不變是一件非常困難的事情。
重構是一種優秀的代碼改進方式,追求不重復的代碼雖然難做到,但是其過程卻可以有效的提高開發團隊的代碼質量,每次對代碼進行的迭代改進,促進了系統簡單的實現。在團隊中提倡使用、甚至半強制性使用重構,有助于分享優秀的軟件設計思路,提高軟件的整體架構。重構還會涉及分析、設計模式、優秀實踐的應用。同時,重構還需要其它優秀實踐的配合,譬如代碼復審和測試優先等等。
4. 系統重構的目標要清晰
重構的訴求、目標要明確,解決什么問題,為什么會出現這些問題,如何解決。解決的方式徹底嗎? 要站在更高的角度看待問題,架構高度、能力決定思維、方式、方法。 如果解決一個問題會帶來更多問題的設計和開發有必要做嗎? 局部的優化可能招致全局受損,在瓶頸之外的任何優化提升都只是幻象。如果資源、人力的消耗在無用功上,表象上是大家多么努力,腐化架構、平臺的工作,那是對公司的不負責、對個人人生的不負責。
5. 架構團隊評審制度的必要性
團隊設計的理論依據是群體決策,與個人決策相比,群體決策的最大好處就是其結論要更加的完整。群體決策需要額外付出溝通成本、決策效率低、責任不明確等。但群體決策如果能夠組織得當,是能夠在架構設計中發揮很大的優勢的。
系統的設計需要召開團隊評審會議進行評審,避免某個問題的設計改造腐化了系統并導致后續的開發、交付、穩定性存在更多隱患。復審是避免設計出現錯誤的重要手段,可在架構設計過程中引入復審的活動。復審應該著重于粗粒度模塊/組件的分類和它們之間的關系。正如后續的重構和穩定性模式所描繪的那樣,保持粗粒度模塊/組件的穩定性有助于重構行為,有助于架構模型的改進。
6. 系統的重構是對原架構的檢視
重構是對構架的合理性、前瞻性、業務敏捷適應性等的綜合考量。對平臺架構,特別是分布式系統架構,要站在整體、全局的視角高度來規劃、重構。當發現系統關鍵基礎模塊、子系統出現問題(擴展性、穩定性、發布的簡易性、資源消耗等等),要審視是否原有架構的設計是否合理,清楚在語言(C/C++/Go/Java/…)之上承載的架構的特征、需要規避的問題。架構問題要在架構層面去解決,宜早不宜晚。
7. 軟件架構設計原則不容模糊
系統內各個代碼模塊的質量很重要,同樣,分布式系統內各個子系統的職責邊界、整體運行、協作效率,穩定性、擴展性、可靠性、容錯性等也亦非常重要。軟件設計原則及模式為最佳實踐,要深入理解和掌握、遵守,但并非要盲從,原則的違背必然會有成本的付出,設計人員要意識這一點,并適時變通補償。具體要結合實際工作中業務、時間、資源及團隊情況來權衡。 另外,軟件的設計原則是針對面向對象設計和編程提出,但并非只適合系統內部對象、結構,對于大規模系統架構仍然有其一定的適用性、可借鑒性。
8. 重構系統以減法思維優先
在軟件系統設計、開發、重構等工程活動中,能做減法的絕對不要做加法。當系統存在問題、需求變化等,多挖掘系統本身的可能性,重構不等于添加模塊,增加屬于慣性思維,添加任何代碼模塊或其它都將導致新的問題出現和面對。要讓系統的架構簡單、清晰、可擴展、穩定、高內聚低耦合,資源利用率最大化,事件擾動最小化,....
要對代碼做減法,要盡可能減少功能,如有疑問則將其刪除/注掉。許多功能可能從未使用,只需為其留一個擴展接口即可。可結合系統需求,有效合理使用一些設計思想、模式可使得程序結構更加合理,代碼更加清晰、消除冗余,減少代碼的壞味道等等。
9. 減少平臺架構內的動態性
動態的東西難于捕捉,系統運行時亦是動態。但對于初始能明確的使用方式,接口形態,基礎數據、配置參數,交互方式等,要避免產生更多變化。譬如,系統在初始就可以聲明約定明確某個信息的唯一性,但一定要在唯一基礎之上產生唯一的 ID, 因為ID是動態生成的,后續對 ID來產生強依賴必將導致系統可擴展性存疑,帶來模塊之間的強耦合性。
10. 規避系統架構的過度設計
架構設計時常會在某些方面過度設計,為了一些根本不會發生的變化而進行一系列復雜的設計,這樣的設計就叫過度設計,往往會帶來資源的浪費并且會增加開發的工作量或難度。系統需要考慮擴展性,可維護性等,但切忌過度設計。需要站在頂層設計高度來判斷哪些設計是過度而規避之。
一個系統/平臺的穩定常規來說都會經歷一個震蕩期,可能跨越數個迭代周期,但最后一定趨向平穩。如后續版本發布、商用投產仍未達到設計的平穩化,仍需不斷進行重構才能適應需求,項目的失敗注定只是時間問題。大的結構性方向錯誤必將導致后續的設計、開發迭代的復雜性,以及穩定、擴展性等的缺失,疊加更多的設計不合理可能。
11. 系統關聯中間件的引入
項目引入中間件,不論該中間件是來自外部或內部自研,要對其功能有非常清晰的認識。在合理范圍內使用,最小依賴原則,關鍵基礎功能引入,對于高級特征的引入使用要權衡和綜合評估。如果引入的中間件成為系統的關鍵支撐項,要最優化場景使用,同時要避免和警惕陷入使用誤區(如某中間件提供核心功能為A,卻被弱化A 而強化使用功能B職責)。中間件自身的依賴件是否與系統自身所集成部分重復,以復用為優先原則。
12. 重構的短期交付與演化方向
短期架構的重構如影響、腐化了整體架構,影響了擴展、穩定性等,要早發現早停止。架構的演變迭代要保持架構的大方向不變。架構的演變需要朝著職責單一、高內聚低耦合,以簡單至上等原則去設計、演化和落地。
13. 架構的簡單并非實現簡單
說到這里,如果大家有一個誤解,認為一個簡單的架構也一定是容易設計的,那就錯了。簡單的架構并不等于實現起來也簡單。簡單的架構需要設計者花費大量的心血,也要求設計者對技術有很深的造詣。
14. 架構師職責并非止于藍圖交付
建筑設計師把設計好的藍圖交給施工人員,施工人員就會按照圖紙建造出一模一樣的大廈。可是,企圖在軟件開發中使用這種模式,這是非常要命的。架構師完全不去深入到第一線怎么知道“地”在哪?怎么才能將設計落的穩當。
架構設計師最易犯的一個問題就是設計和代碼的脫離,即使在設計階段考慮得非常完美的架構,在編碼階段也會出現這樣或那樣的問題,從而導致架構實現變得復雜。或者說,出現了壞味道,重構的技巧也同樣有助于識別壞味道。讓設計師參與核心代碼的編寫或進行代碼審核,以確保編碼者真正了解了架構設計的意圖。
15.....
架構設計的核心還是方法論,簡單的設計并不等同于較少的付出。往往需要對現實世界的抽象,看似簡單,但實現起來卻需大量的業務和系統知識、很強的設計能力。因此,做到簡單是程序員不斷追尋的目標之一。
模式是一種指導,有助于做出一個優良的設計方案,達到事半功倍的效果。模式也是面向對象設計的基石,但模式常扮演著過度設計的角色。在設計之初少關注模式的適用,把精力放在如何滿足需求上,而在設計迭代演進中重構到模式以擴展或演變為軟件設計的基礎,提升靈活性,避免導致過度或不充分的設計。
… …
八. 最? 后
軟件設計是門藝術,是門劃分邊界的藝術。
軟件設計不只屬于程序設計,更像是一種藝術創作的思維 …
優秀的架構設計需要架構的目的和方向,
需要架構設計師的統籌全局、深入需求,需要抽象、演化式架構設計思維,
需要架構設計師的不懈努力和對細節的把握,以及充分的、前瞻性的預見性,
需要數據模型的準確、完整、規范、一致以及標準化,
需要清晰的邊界劃分,需要模塊的職責單一,需要信息專家模式,
需要系統的高內聚、低耦合,通訊鏈路關系的最簡化,
需要致力于能量消耗低限度,將消耗單位能量產生的熵提高到最大限度,
需要面向系統化組織的設計,需要團隊的設計、協作…
......
軟件系統的品質,更多取決于架構的優劣;
決于思維設計的高度與深度:抽象分治,高聚低耦,簡單至上,模式加持,演化迭代, ...
可信構架,架構至簡,唯美藝術 !
軟件開發 運維
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。