怎樣成為一個優秀的架構師?資深技術專家告訴你

2019-10-08     IT技術分享

怎樣才算是架構師?

架構師是一個既能掌控整體又能洞悉局部瓶頸並依據具體的業務場景給出解決方案的團隊領導型人物。看似完美的「人格模型」背後,是艱辛的探索。

架構師不是一個人,他需要建立高效卓越的體系,帶領團隊去攻城略地,在規定的時間內完成項目。

架構師的分類

從業界來看對於架構師的理解可以大概區分為:

  • 企業架構師:專注於企業總體 IT 架構的設計。
  • IT 架構師-軟體產品架構師:專注於軟體產品的研發。
  • IT 架構師-應用架構師:專注於結合企業需求,定製化 IT 解決方案;大部分需要交付的工作包括總體架構、應用架構、數據架構,甚至部署架構。
  • IT 架構師-技術架構師:專注於基礎設施,某種軟硬體體系,甚至雲平台,提交:產品建議、產品選型、部署架構、網絡方案,甚至數據中心建設方案等。

架構師的職責

架構師需要能夠識別定義並確認需求,能夠進行系統分解形成整體架構,能夠正確地技術選型,能夠制定技術規格說明並有效推動實施落地。

按 TOGAF 的定義,架構師的職責是了解並關注實際上關係重大但未變得過載的一些關鍵細節和介面,架構師的角色有:理解並解析需求,創建有用的模型,確認、細化並擴展模型,管理架構。

從項目視圖看:

對接管理部門:彙報技術方案,進度;技術溝通;

對接客戶 PM,項目 PM:協助項目計劃,人員管理等。負責所有技術交付物的指導;

對接業務部門和需求人員:了解和挖掘痛點,幫忙梳理高級業務需求,指導需求工藝;

對接開發:產品支持、技術指導、架構指導;

對接測試:配合測試計劃和工藝制定。配合性能測試或者非功能性測試;

對接運維:產品支持,運維支持;

對接配置&環境:產品支持;

.......

架構原則

設計原則就是架構設計的指導思想,它指導我們如何將數據和函數組織成類,如何將類連結起來成為組件和程序。反向來說, 架構的主要工作就是將軟體拆解為組件 ,設計原則指導我們如何拆解、拆解的粒度、組件間依賴的方向、組件解耦的方式等。

設計原則有很多,我們進行架構設計的主導原則是 OCP(開閉原則),在類和代碼的層級上有:SRP(單一職責原則)、LSP(里氏替換原則)、ISP(接口隔離原則)、DIP(依賴反轉原則);在組件的層級上有:REP(復用、發布等同原則)、CCP(共同閉包原則)、CRP(共同復用原則),處理組件依賴問題的三原則:無依賴環原則、穩定依賴原則、穩定抽象原則。

設計原則

1、OCP(開閉原則): 設計良好的軟體應該易於擴展,同時抗拒修改。這是我們進行架構設計的主導原則,其他的原則都為這條原則服務。

2、SRP(單一職責原則): 任何一個軟體模塊,都應該有且只有一個被修改的原因,「被修改的原因「指系統的用戶或所有者,翻譯一下就是,任何模塊只對一個用戶的價值負責,該原則指導我們如何拆分組件。

舉個例子,CTO 和 COO 都要統計員工的工時,當前他們要求的統計方式可能是相同的,我們復用一套代碼,這時 COO 說周末的工時統計要乘以二,按照這個需求修改完代碼,CTO 可能就要過來罵街了。當然這是個非常淺顯的例子,實際項目中也有很多代碼服務於多個價值主體,這帶來很大的探秘成本和修改風險,另外,當一份代碼有多個所有者時,就會產生代碼合併衝突的問題。

3、LSP(里氏替換原則):當 用同一接口的不同實現互相替換時,系統的行為應該保持不變。該原則指導的是接口與其實現方式。

你一定很疑惑,實現了同一個接口,他們的行為也肯定是一致的呀,還真不一定。假設認為矩形的系統行為是:面積=寬*高,讓正方形實現矩形的接口,在調用 setW 和 setH 時,正方形做的其實是同一個事情,設置它的邊長。這時下邊的單元測試用矩形能通過,用正方形就不行,實現同樣的接口,但是系統行為變了,這是違反 LSP 的經典案例。

Rectangle r = ... 
r.setW(5);
r.setH(2);
assert(r.area() == 10);

4、ISP(接口隔離原則): 不依賴任何不需要的方法、類或組件。該原則指導我們的接口設計。當我們依賴一個接口但只用到了其中的部分方法時,其實我們已經依賴了不需要的方法或類,當這些方法或類有變更時,會引起我們類的重新編譯,或者引起我們組件的重新部署,這些都是不必要的。所以我們最好定義個小接口,把用到的方法拆出來。

5、DIP(依賴反轉原則): 指一種特定的解耦(傳統的依賴關係創建在高層次上,而具體的策略設置則應用在低層次的模塊上)形式,使得高層次的模塊不依賴於低層次的模塊的實現細節,依賴關係被顛倒(反轉),從而使得低層次模塊依賴於高層次模塊的需求抽象。

跨越組建邊界的依賴方向永遠與控制流的方向相反。該原則指導我們設計組件間依賴的方向。

依賴反轉原則是個可操作性非常強的原則,當你要修改組件間的依賴方向時,將需要進行組件間通信的類抽象為接口,接口放在邊界的哪邊,依賴就指向哪邊。

6、REP(復用、發布等同原則): 軟體復用的最小粒度應等同於其發布的最小粒度。直白地說,就是要復用一段代碼就把它抽成組件,該原則指導我們組件拆分的粒度。

7、CCP(共同閉包原則): 為了相同目的而同時修改的類,應該放在同一個組件中。CCP 原則是 SRP 原則在組件層面的描述。該原則指導我們組件拆分的粒度。

對大部分應用程式而言,可維護性的重要性遠遠大於可復用性,由同一個原因引起的代碼修改,最好在同一個組件中,如果分散在多個組件中,那麼開發、提交、部署的成本都會上升。

8、CRP(共同復用原則): 不要強迫一個組件依賴它不需要的東西。CRP 原則是 ISP原則在組件層面的描述。該原則指導我們組件拆分的粒度。

相信你一定有這種經歷,集成了組件 A,但組件 A 依賴了組件 B、C。即使組件 B、C 你完全用不到,也不得不集成進來。這是因為你只用到了組件 A 的部分能力,組件 A 中額外的能力帶來了額外的依賴。如果遵循共同復用原則,你需要把 A 拆分,只保留你要用的部分。

REP、CCP、CRP 三個原則之間存在彼此競爭的關係,REP 和 CCP 是黏合性原則,它們會讓組件變得更大,而 CRP 原則是排除性原則,它會讓組件變小。遵守REP、CCP 而忽略 CRP,就會依賴了太多沒有用到的組件和類,而這些組件或類的變動會導致你自己的組件進行太多不必要的發布;遵守 REP、CRP 而忽略 CCP,因為組件拆分的太細了,一個需求變更可能要改 n 個組件,帶來的成本也是巨大的。

指導原則

除了上述設計原則,還有一些重要的指導原則如下:

1、N+1設計: 系統中的每個組件都應做到沒有單點故障;

2、回滾設計: 確保系統可以向前兼容,在系統升級時應能有辦法回滾版本;

3、禁用設計: 應該提供控制具體功能是否可用的配置,在系統出現故障時能夠快速下線功能;

4、監控設計: 在設計階段就要考慮監控的手段,便於有效的排查問題,比如引入traceId、業務身份 Id 便於排查監控問題;

5、多活數據中心設計: 若系統需要極高的高可用,應考慮在多地實施數據中心進行多活,至少在一個機房斷電的情況下系統依然可用;

6、採用成熟的技術: 剛開發的或開源的技術往往存在很多隱藏的 bug,出了問題沒有很好的商業支持可能會是一個災難;

7、資源隔離設計: 應避免單一業務占用全部資源;

8、架構水平擴展設計: 系統只有做到能水平擴展,才能有效避免瓶頸問題;

9、非核心則購買的原則: 非核心功能若需要占用大量的研發資源才能解決,則考慮購買成熟的產品;

10、使用商用硬體: 商用硬體能有效降低硬體故障的機率;

11、快速疊代: 系統應該快速開發小功能模塊,儘快上線進行驗證,早日發現問題大大降低系統交付的風險;

12、無狀態設計: 服務接口應該做成無狀態的,當前接口的訪問不依賴於接口上次訪問的狀態。

架構師知道了職責,具備很好的架構思維,掌握了通用的架構框架和方法論,使用架構原則進行架構設計,不同的業務和系統要求不一樣,那麼有沒有針對不同場景的系統架構設計?下文就針對分布式架構演進、單元化架構、面向服務 SOA 架構、微服務架構、Serverless 架構進行介紹,以便於我們在實際運用中進行參考使用。

具備架構師的思維

架構師職責明確了,那麼有什麼架構思維可以指導架構設計呢?請看下述的架構思維。

1、自頂向下構建架構

要點主要如下:

1)首先定義問題,而定義問題中最重要的是定義客戶的問題。定義問題,特別是識別出關鍵問題,關鍵問題是對客戶有體感,能夠解決客戶痛點,通過一定的數據化來衡量識別出來,關鍵問題要優先給出解決方案。

2)問題定義務必加入時間維度,把手段/方案和問題定義區分開來。

3)問題定義中,需要對問題進行升層思考後再進行升維思考,從而真正抓到問題的本質,理清和挖掘清楚需求;要善用第一性原理思維進行分析思考問題。

4)問題解決原則:先解決客戶的問題(使命),然後才能解決自己的問題(願景);務必記住不是強調我們怎麼樣,而是我們能為客戶具體解決什麼問題,然後才是我們變成什麼,從而怎麼樣去更好得服務客戶。

5)善用多種方法對客戶問題進行分析,轉換成我們產品或者平台需要提供的能力,比如倉儲系統 WMS 可以提供哪些商業能力。

6)對我們的現有的流程和能力模型進行梳理,找到需要提升的地方,升層思考和升維思考真正明確提升部分。

7)定義指標,並能夠對指標進行拆解,然後進行數學建模。

8)將抽象出來的能力訴求轉換成技術挑戰,此步對於技術人員來說相當於找到了靶子,可以進行方案的設計了,需要結合自底向上的架構推導方式。

9)創新可以是業務創新,也可以是產品創新,也可以是技術創新,也可以是運營創新,升層思考、升維思考,使用第一性原理思維、生物學(進化論--進化=變異+選擇+隔離、熵增定律、分形和湧現)思維等哲科思維可以幫助我們在業務,產品,技術上發現不同的創新可能。可以說哲科思維是架構師的靈魂思維。

2、自底向上推導應用架構

先根據業務流程,分解出系統時序圖,根據時序圖開始對模塊進行歸納,從而得到粒度更大的模塊,模塊的組合/聚合構建整個系統架構。

基本上應用邏輯架構的推導有4個子路徑,他們分別是:

  • 業務概念架構:業務概念架構來自於業務概念模型和業務流程;
  • 系統模型:來自於業務概念模型;
  • 系統流程:來自業務流程;
  • 非功能性的系統支撐:來自對性能、穩定性、成本的需要。

效率、穩定性、性能是最影響邏輯架構落地成物理架構的三大主要因素,所以從邏輯架構到物理架構,一定需要先對效率、穩定性和性能做出明確的量化要求。

自底向上重度依賴於演繹和歸納。

如果是產品方案已經明確,程式設計師需要理解這個業務需求,並根據產品方案推導出架構,此時一般使用自底向上的方法,而領域建模就是這種自底向上的分析方法。

對於自底向上的分析方法,如果提煉一下關鍵詞,會得到如下兩個關鍵詞:

1)演繹: 演繹就是邏輯推導,越是底層的,越需要演繹:

  • 從用例到業務模型就屬於演繹;
  • 從業務模型到系統模型也屬於演繹;
  • 根據目前的問題,推導出要實施某種穩定性措施,這是也是演繹。

2)歸納: 這裡的歸納是根據事物的某個維度來進行歸類,越是高層的,越需要歸納:

  • 問題空間模塊劃分屬於歸納;
  • 邏輯架構中有部分也屬於歸納;
  • 根據一堆穩定性問題,歸納出,事前,事中,事後都需要做對應的操作,是就是根據時間維度來進行歸納。

3、領域驅動設計架構

大部分傳統架構都是基於領域模型分析架構,典型的領域實現模型設計可以參考DDD(領域驅動設計),詳細可以參考《實現領域驅動設計》這本書,另外《UML和模式應用》在領域建模實操方面比較好,前者偏理論了解,後者便於落地實踐。

領域劃分設計步驟:

(1) 對用戶需求場景分析,識別出業務全維度 Use Case。

(2) 分析模型魯棒圖,識別出業務場景中所有的實體對象。魯棒圖 —— 是需求設計過程中使用的一種方法(魯棒性分析),通過魯棒分析法可以讓設計人員更清晰,更全面地了解需求。它通常使用在需求分析後及需求設計前做軟體架構分析之用,它主要注重於功能需求的設計分析工作。需求規格說明書為其輸入信息,設計模型為其輸出信息。它是從功能需求向設計方案過渡的第一步,重點是識別組成軟體系統的高級職責模塊、規劃模塊之間的關係。魯棒圖包含三種圖形:邊界、控制、實體,三個圖形如下:

(3) 領域劃分,將所有識別出的實體對象進行分類。

(4) 評估域劃分合理性,並進行優化。

4、基於數據驅動設計架構

隨著 IoT、大數據和人工智慧的發展,以領域驅動的方式進行架構往往滿足不了需求或者達不到預期的效果,在大數據時代,在大數據應用場景,我們需要轉變思維,從領域分析升維到基於大數據統計分析結果來進行業務架構、應用架構、數據架構和技術架構。這裡需要架構師具備數理統計分析的基礎和 BI 的能力,以數據思維來架構系統,典型的系統像阿里的數據分析平台采雲間和菜鳥的數據分析平台 FBI。

上述四種思維,往往在架構設計中是融合使用的,需要根據業務或者系統的需求來選擇側重思維方式。

單元化架構,微服務架構以及 Serveless 架構

單元化架構

1. 單元化是什麼

單元化架構是從並行計算領域發展而來。在分布式服務設計領域,一個單元(Cell)就是滿足某個分區所有業務操作的自包含的安裝。而一個分區(Shard),則是整體數據集的一個子集,如果你用尾號來劃分用戶,那同樣尾號的那部分用戶就可以認為是一個分區。單元化就是將一個服務設計改造讓其符合單元特徵的過程。

單元化架構,為什麼要用以及我們如何做到

圖 1 :洋蔥細胞的顯微鏡截圖,單元化要達到的目的就是讓每個單元像細胞一樣獨立工作

在傳統的服務化架構下(如下圖),服務是分層的,每一層使用不同的分區算法,每一層都有不同數量的節點,上層節點隨機選擇下層節點。當然這個隨機是比較而言的。

單元化架構,為什麼要用以及我們如何做到

圖 2 :傳統的服務化架構,為伸縮性設計,上層節點隨機選擇下層節點

與其不同的是,在單元化架構下,服務雖然分層劃分,但每個單元自成一體。按照層次來講的話,所有層使用相同的分區算法,每一層都有相同數量的節點,上層節點也會訪問指定的下層節點。因為他們已經在一起。

單元化架構,為什麼要用以及我們如何做到

圖 3 :單元化架構,為性能和隔離性而設計,上層節點訪問指定下層節點

2. 為什麼要用單元化

在性能追求和成本限制的情況下,我們需要找到一種合適的方法來滿足服務需求。在傳統的分布式服務設計,我們考慮的更多是每個服務的可伸縮性,當各個服務獨立設計時你就要在每一層進行伸縮性的考慮。這是服務化設計(SOA)流行的原因,我們需要每個服務能夠單獨水平擴展。

但是在摩爾定律下,隨著硬體的不斷升級,計算機硬體能力已經越來越強,CPU 越來越快,內存越來越大,網絡越來越寬。這讓我們看到了在單台機器上垂直擴展的機會。尤其是當你遇到一個性能要求和容量增長可以預期的業務,單元化給我們提供另外的機會,讓我們可以有效降低資源的使用,提供更高性能的服務。

總體而言,更高性能更低成本是我們的主要目標,而經過單元化改造,我們得以用更少(約二分之一)的機器,獲得了比原來更高(接近百倍)的性能。性能的提升很大部分原因在於服務的本地化,而服務的集成部署又進一步降低了資源的使用。

當然除了性能收益,如果你做到了,你會發現還有很多收益,比如更好的隔離性,包括請求隔離和資源隔離,比如更友好的升級,產品可以灰度發布等。單元化改造後對高峰的應對以及擴容方式等問題,各位可以參考#微博春節技術保障系列#中的單元化架構文章,也不在此一一贅述。

3. 我們如何做到

此次單元化改造基於微博現有的業務,因此這裡也先行介紹一下。粉絲服務平台是微博的內容推送系統(代號 Castalia),可為 V 用戶提供向其粉絲推送高質量內容的高速通道(單元化之後已到達百萬條每秒)。整個服務涉及用戶篩選、發送計費、屏蔽檢查、限流控制和消息群發等多個子服務。由於改造思想相通,這裡以用戶篩選和消息群發兩個服務為例,下面兩圖分別為商業群發在服務化思想和單元化思想下不同的架構。

單元化架構,為什麼要用以及我們如何做到

圖 4:服務化思想下的商業群發架構設計(舊版)

單元化架構,為什麼要用以及我們如何做到

圖 5 :商業群發在單元化思想下的架構設計(新版)

對於篩選服務,在服務化架構里,需要去粉絲服務獲取粉絲關係,然後去特徵服務進行用戶特徵篩選,最後將篩選結果傳輸到群發伺服器上;而在單元化架構里,粉絲關係直接就在本地文件中,用戶特徵服務也在本地,最後的篩選結果再不需要傳輸。服務本地化(粉絲關係和用戶特徵存儲)減去了網絡開銷,降低了服務延時,還同時提高了訪問速度和穩定性,而篩選結果本地存儲又進一步節省了帶寬並降低了延遲。以百萬粉絲為例,每次網絡操作的減少節省帶寬 8M 左右,延時也從 400ms 降為 0。

群發服務同樣如此。由於在服務化架構里,我們使用 MySQL 和 Memcache 的方案,由於關係數據庫的寫入性能問題,中間還有隊列以及相應的隊列處理機,所有四個模塊都有單獨的機器提供服務,而在單元化架構里,四合一之後,只需要一套機器。當然機器的配置可能會有所提升,但真正計算之後你就會發現其實影響微乎其微。原因除了前面介紹的硬體增長空間外,上架機器的基本配置變高也是一個原因。而且,在單元化方案里,當我們把緩存部署在本地之後,其性能還有了額外的 20% 提升。

一些業務特有問題

不過群發這個場景,我們也遇到了一些特定的問題,一是分區問題,一是作業管理。這裡也與各位分享下我們的解決方法。

  1. 分區問題
  2. 分區問題其實是每個服務都會遇到的,但單元化後的挑戰在於讓所有服務都適配同一分區算法,在我們的場景下,我們按照接收者進行了分區,即從底層往上,每一層都來適配此分區算法。
  3. 這裡有特例的是用戶特徵和屏蔽服務,由於總體容量都很小,我們就沒有對數據進行分區,所有單元內都是同一套全量數據,都是一個外部全量庫的從庫。不過由於本單元內的上層服務的關係,只有屬於本分區的用戶數據被訪問到。所以,適配同一分區算法在某種程度上講,可以兼容即可。
  4. 作業管理
  5. 按照前面的分區方式,將群發服務的整體架構變成了一個類似 Scatter-Gather+CQRS 的方案,因為 Gather 不是一個請求處理的必須要素。也就是說,一個群發請求會被擴散到所有單元中,每個單元都要針對自己分區內的用戶處理這個群發請求。
  6. 廣播方式的引入,使得我們首先需要在前端機進行分單元作業的處理監控,我們在此增加了持久化隊列來解決。同時,由於單元內每個服務也都是單獨維護的,作業可能在任何時間中斷,因此每個作業在單元內的狀態也都是有記錄的,以此來達到作業的可重入和冪等性,也就可以保證每個作業都可以在任何時間重做,但不會重複執行。

除此之外,我們還對伺服器進行了更為精細的控制,使用 CPU 綁定提高多服務集成部署時的整體效率,使用多硬碟設計保證每個服務的 IO 性能,通過主從單元的讀寫分離來提高整體服務等等。

參考文章:https://www.infoq.cn/article/how-weibo-do-unit-architecture/

SOA架構

SOA(Service-Oriented Architecture,面向服務的架構)是一個組件模型,它將應用程式的不同功能單元(稱為服務)通過這些服務之間定義良好的接口和契約聯繫起來。接口是採用中立的方式進行定義的,它應該獨立於實現服務的硬體平台、作業系統和程式語言。這使得構建在各種各樣的系統中的服務可以以一種統一和通用的方式進行交互。面向服務架構,它可以根據需求通過網絡對鬆散耦合的粗粒度應用組件進行分布式部署、組合和使用。服務層是 SOA 的基礎,可以直接被應用調用,從而有效控制系統中與軟體代理交互的人為依賴性。

SOA的實施具有幾個鮮明的基本特徵。實施 SOA 的關鍵目標是實現企業 IT 資產的最大化作用。要實現這一目標,就要在實施 SOA 的過程中牢記以下特徵:

  • 可從企業外部訪問;
  • 隨時可用;
  • 粗粒度的服務接口分級;
  • 鬆散耦合;
  • 可重用的服務;
  • 服務接口設計管理;
  • 標準化的服務接口;
  • 支持各種消息模式;
  • 精確定義的服務契約。

為了實現 SOA,企業需要一個服務架構,下圖顯示了一個例子:

在上圖中, 服務消費者(service consumer)可以通過發送消息來調用服務。這些消息由一個服務總線(service bus)轉換後發送給適當的服務實現。這種服務架構可以提供一個業務規則引(business rules engine),該引擎容許業務規則被合併在一個服務里或多個服務里。這種架構也提供了一個服務管理基礎(service management infrastructure),用來管理服務,類似審核,列表(billing),日誌等功能。

此外,該架構給企業提供了靈活的業務流程,更好地處理控制請求(regulatory requirement),例如Sarbanes Oxley(SOX),並且可以在不影響其他服務的情況下更改某項服務。

微服務架構

先來看看傳統的 web 開發方式,通過對比比較容易理解什麼是 Microservice Architecture。和 Microservice 相對應的,這種方式一般被稱為 Monolithic(單體式開發)。

所有的功能打包在一個 WAR包里,基本沒有外部依賴(除了容器),部署在一個JEE容器(Tomcat,JBoss,WebLogic)里,包含了 DO/DAO,Service,UI 等所有邏輯。

1、優點:

  • 開發簡單,集中式管理;
  • 基本不會重複開發;
  • 功能都在本地,沒有分布式的管理和調用消耗。

2、缺點:

  • 效率低:開發都在同一個項目改代碼,相互等待,衝突不斷;
  • 維護難:代碼功功能耦合在一起,新人不知道何從下手;
  • 不靈活:構建時間長,任何小修改都要重構整個項目,耗時;
  • 穩定性差:一個微小的問題,都可能導致整個應用掛掉;
  • 擴展性不夠:無法滿足高並發下的業務需求。

3、常見的系統架構遵循的三個標準和業務驅動力:

  • 提高敏捷性:及時響應業務需求,促進企業發展;
  • 提升用戶體驗:提升用戶體驗,減少用戶流失;
  • 降低成本:降低增加產品、客戶或業務方案的成本。

4、基於微服務架構的設計:

目的:有效的拆分應用,實現敏捷開發和部署。

關於微服務的一個形象表達:

  • X軸:運行多個負載均衡器之後的運行實例;
  • Y軸:將應用進一步分解為微服務(分庫);
  • Z軸:大數據量時,將服務分區(分表)。

5、SOA和微服務的區別:

  • SOA喜歡重用,微服務喜歡重寫;
  • SOA喜歡水平服務,微服務喜歡垂直服務;
  • SOA喜歡自上而下,微服務喜歡自下而上。

Serverless架構

1、思想: 無伺服器是一種架構理念,其核心思想是 將提供服務資源的基礎設施抽象 成各種服務,以 API 接口的方式供給用戶按需調用,真正做到按需伸縮、按使用收費。

2、優勢: 消除了對傳統的海量持續在線伺服器組件的需求,降低了開發和運維的複雜性,降低運營成本並縮短了業務系統的交付周期,使得用戶能夠專注在價值密度更高的業務邏輯的開發上。

3、內容: 目前業界較為公認的無伺服器架構主要包括兩個方面,即提供計算資源的函數服務平台 FaaS,以及提供託管雲服務的後端服務 BaaS。

函數即服務(Function as a Service):是一項基於事件驅動的函數託管計算服務。通過函數服務,開發者只需要編寫業務函數代碼並設置運行的條件,無需配置和管理伺服器等基礎設施,函數代碼運行在無狀態的容器中,由事件觸發且短暫易失,並完全由第三方管理,基礎設施對應用開發者完全透明。函數以彈性、高可靠的方式運行,並且按實際執行資源計費,不執行不產生費用。

後端即服務(Backend as a Service):BaaS 覆蓋了應用可能依賴的所有第三方服務,如雲資料庫、身份驗證、對象存儲等服務,開發人員通過 API 和由 BaaS 服務商提供的 SDK,能夠集成所需的所有後端功能,而無需構建後端應用,更不必管理虛擬機或容器等基礎設施,就能保證應用的正常運行。

三個less感覺很好:

  • Codeless 對應的是服務開發,實現了原始碼託管,你只需要關注你的代碼實現,而不需要關心你的代碼在哪,因為在整個開發過程中你都不會感受到代碼庫和代碼分支的存在。
  • Applicationless 對應的是服務發布,在服務化框架下,你的服務發布不再需要申請應用,也不需要關注你的應用在哪。
  • Serverless 對應的則是服務運維,有了 Serverless 化能力,你不再需要關注你的機器資源,Servlerless 會幫你搞定機器資源的彈性擴縮容。

架構師在完成上述架構設計後,最終是需要協同利益相關方一起按項目化運作落地拿結果,那麼應該如何保證利益相關方在項目落地的滿意度,如何保證按照架構很好的拿到項目成功的結果呢?架構管理能力是架構師非常重要的能力。

架構師管理 架構雙贏模型

架構結果管理

優秀架構師必須掌握的幾種架構思維

架構的本質是管理複雜性,抽象、分層、分治和演化思維是我們工程師/架構師應對和管理複雜性的四種最基本武器。

最近團隊來了一些新人,有些有一定工作經驗,是以高級工程師/架構師身份進來的,但我發現他們大部分人思維偏應用和細節,抽象能力弱。所以作為團隊技術培訓的一部分,我整理了這篇文章,希望對他們樹立正確的架構設計思維有幫助。我認為,對思維習慣和思考能力的培養,其重要性遠遠大於對實際技術工具的掌握。

由於文章內容較長,所以我把它分成兩篇小文章,在第一篇《優秀架構師必須掌握的架構思維》中,我會先介紹抽象、分層、分治和演化這四種應對複雜性的基本思維。在第二篇《四個架構設計案例及其思維方式》中,我會通過四個案例,講解如何綜合運用這些思維,分別對小型系統,中型系統,基礎架構,甚至是組織技術體系進行架構和設計。

一、抽象思維

如果要問軟體研發/系統架構中最重要的能力是什麼,我會毫不猶豫回答是抽象能力。抽象(abstraction)這個詞大家經常聽到,但是真正理解和能講清楚什麼是抽象的人少之又少。抽象其實是這樣定義的:

對某種事物進行簡化表示或描述的過程,抽象讓我們關注要素,隱藏額外細節。

舉一個例子,見下圖:

你看到什麼?你看到的是一扇門,對不對?你看到的不是木頭,也不是碳原子,這個門就是抽象,而木頭或者碳原子是細節。另外你可以看到門上有個門把手,你看到的不是鐵,也不是鐵原子,門把手就是抽象,鐵和鐵原子是細節。

在系統架構和設計中,抽象幫助我們從大處著眼(get our mind about big picture),隱藏細節(temporarily hide details)。抽象能力的強弱,直接決定我們所能解決問題的複雜性和規模大小。

下圖是我們小時候玩的積木,我發現小時候喜歡玩搭積木的,並且搭得快和好的小朋友,一般抽象能力都比較強。

上圖右邊的積木城堡就是抽象,這個城堡如果你細看的話,它其實還是由若干個子模塊組成,這些模塊是子抽象單元,左邊的各種形狀的積木是細節。搭積木的時候,小朋友腦袋裡頭先有一個城堡的大圖(抽象),然後他/她大腦裡頭會有一個初步的子模塊分解(潛意識中完成),然用利用積木搭建每一個子模塊,最終拼裝出最後的城堡。這裡頭有一個自頂向下的分治設計,然後自底向上的組合過程,這個分治思維非常重要,我們後面會講。

我認為軟體系統架構設計和小朋友搭積木無本質差異,只是解決的問題域和規模不同罷了。架構師先要在大腦中形成抽象概念,然後是子模塊分解,然後是依次實現子模塊,最後將子模塊拼裝組合起來,形成最後系統。所以我常說編程和架構設計就是搭積木,優秀的架構師受職業習慣影響,眼睛裡看到的世界都是模塊化拼裝組合式的。

抽象能力不僅對軟體系統架構設計重要,對建築、商業、管理等人類其它領域活動同樣非常重要。其實可以這樣認為,我們生存的世界都是在抽象的基礎上構建起來的,離開抽象人類將寸步難行。

這裡順便提一下抽象層次跳躍問題,這個在開發中是蠻普遍的。有經驗的程式設計師寫代碼會保持抽象層次的一致性,代碼讀起來像講故事,比較清晰易於理解;而沒有經驗的程式設計師會有明顯的抽象層次跳躍問題,代碼讀起來就比較累,這個是抽象能力不足造成。舉個例子:

一個電商網站在處理訂單時,一般會走這樣一個流程:

  1. 更新庫存(InventoryUpdate)
  2. 打折計算(Discounting)
  3. 支付卡校驗(PaycardVerification)
  4. 支付(Pay)
  5. 送貨(Shipping)

上述流程中的抽象是在同一個層次上的,比較清晰易於理解,但是沒有經驗的程式設計師在實現這個流程的時候,代碼層次會跳,比方說主流程到支付卡校驗一塊,他的代碼會突然跳出一行某銀行API遠程調用,這個就是抽象跳躍,銀行API調用是細節,應該封裝在PaycardVerification這個抽象裡頭。

二、分層思維

除了抽象,分層也是我們應對和管理複雜性的基本思維武器,如下圖,為了構建一套複雜系統,我們把整個系統劃分成若干個層次,每一層專註解決某個領域的問題,並向上提供服務。有些層次是縱向的,它貫穿所有其它層次,稱為共享層。分層也可以認為是抽象的一種方式,將系統抽象分解成若干層次化的模塊。

分層架構的案例很多,一個中小型的Spring Web應用程式,我們一般會設計成三層架構:

作業系統是經典的分層架構,如下圖:

TCP/IP協議棧也是經典的分層架構,如下圖:

如果你關注人類文明演化史,你會發現今天的人類世界也是以分層方式一層層搭建和演化出來的。今天的網際網路系統可以認為是現代文明的一個層次,其上是基於網際網路的現代商業,其下是現代電子工業基礎設施,諸如此類。

三、分治思維

分而治之(divide and combine或者split and merge)也是應對和管理複雜性的一般性方法,下圖展示一個分治的思維流程:

對於一個無法一次解決的大問題,我們會先把大問題分解成若干個子問題,如果子問題還無法直接解決,則繼續分解成子子問題,直到可以直接解決的程度,這個是分解(divide)的過程;然後將子子問題的解組合拼裝成子問題的解,再將子問題的解組合拼裝成原問題的解,這個是組合(combine)的過程。

面試時為了考察候選人的分治思維,我經常會面一個分治題:給你一台8G內存/500G磁碟空間的普通電腦,如何對一個100G的大文件進行排序?假定文件中都是字符串記錄,一行約100個字符。

這是一個典型的分治問題,100G的大文件肯定無法一次加載到內存直接排序,所以需要先切分成若干小問題來解決。那麼8G內存的計算機一次大概能排多大的數據量,可以在有限的時間內排完呢?也就是100G的大文件要怎麼切法,切成多少份比較合適?這個是考察候選人的時間空間複雜度估算能力,需要一定的計算機組織和算法功底,也需要一定實戰經驗和sense。實際上8G內存的話,作業系統要用掉一部分,如果用Java開發排序程序,大致JVM可用2~4G內存,基於一般的經驗值,一次排1G左右的數據應該沒有問題(我實際在計算機上干過1G數據的排序,是OK的)。所以100G的文件需要先切分成100份,每份1G,這樣每個子文件可以直接加載到內存進行排序。對於1G數據量的字符串排序,採用Java裡頭提供的快速排序算法是比較合適的。

好,經過有限時間的排序(取決於計算機性能,快的一天內能排完),假定100個1G的文件都已經排好了,相當於現在硬碟上有100個已經排好序的文件,但是我們最終需要的是一個排好序的文件,下面該怎麼做?這個時候我們需要把已經解決的子問題組合起來,合併成我們需要的最終結果文件。這個時候該採用什麼算法呢?這裡考察候選人對外排序和歸併排序算法的掌握程度,我們可以將100個排好序的文件進行兩兩歸併排序,這樣不斷重複,我們就會得到50個排好序的文件,每個大小是2G。然後再兩兩歸併,不斷重複,直到最後兩個文件歸併成目標文件,這個文件就是100G並且是排好序的。因為是外排序+歸併排序,每次只需要讀取當前索引指向的文件記錄到內存,進行比較,小的那個輸出到目標文件,內存占用極少。另外,上面的算法是兩路歸併,也可以採用多路歸併,甚至是採用堆排序進行優化,但是總體分治思路沒有變化。

總體上這是一個非常好的面試題,除了考察候選人的分治思維之外,還考察對各種排序算法(快排,外排序,歸併排序,堆排序)的理解,計算的時間空間複雜度估算,計算機的內外存特性和組織,文件操作等等。實際上能完全回答清楚這個問題的候選人極少,如果有幸被我面到一個,我會如獲至寶,因為這個人有成長為優秀架構師的潛質。

另外,遞歸也是一種特殊的分治技術,掌握遞歸技術的開發人員,相當於掌握了一種強大的編程武器,可以解決一些一般開發人員無法解決的問題。比方說最近我的團隊在研發一款新的服務框架,其中包括契約解析器(parser),代碼生產器(code generator),序列化器(serializer)等組件,裡頭大量需要用到遞歸的思維和技術,沒有這個思維的開發人員就幹不了這個事情。所以我在面試候選人的時候,一般都會出遞歸相關的編程題,考察候選人的遞歸思維。

大自然中遞歸結構比比皆是,如下圖,大家有興趣不妨思考,大自然通過遞歸給我們人類何種啟示?

四、演化思維

社區裡頭經常有人在討論:架構是設計出來的?還是演化出來的?我個人基於十多年的經驗認為,架構既是設計出來的,同時也是演化出來的,對於網際網路系統,基本上可以說是三分設計,七分演化,而且是在設計中演化,在演化中設計,一個不斷疊代的過程。

在網際網路軟體系統的整個生命周期過程中,前期的設計和開發大致只占三分,在後面的七分時間裡,架構師需要根據用戶的反饋對架構進行不斷的調整。我認為架構師除了要利用自身的架構設計能力,同時也要學會藉助用戶反饋和進化的力量,推動架構的持續演進,這個就是演化式架構思維。

當然一開始的架構設計非常重要,架構定系統基本就成型了,不容馬虎。同時,優秀的架構師深知,能夠不斷應對環境變化的系統,才是有生命力的系統,架構的好壞,很大部分取決於它應對變化的靈活性。所以具有演化式思維的架構師,能夠在一開始設計時就考慮到後續架構的演化特性,並且將靈活應對變化的能力作為架構設計的主要考量。

當前,社區正在興起一種新的架構方法學~演化式架構,微服務架構就是一種典型的演化式架構,它能夠快速響應市場用戶需求的變化,而單塊架構就缺乏這種靈活性。馬丁·福樂曾經在其博客上給出過一張微服務架構的演化路線圖[附錄8.2],可以用來解釋設計式思維和演化式思維的差異,如下圖所示:

上面的路線是一開始就直奔微服務架構,其實背後體現的是設計式架構的思維,認為架構師可以完全設計整個系統和它的演化方向。馬丁認為這種做法風險非常高,一個是成本高昂,另外一個是剛開始架構師對業務域理解不深,無法清晰劃分領域邊界,開發出來的系統很可能無法滿足用戶需求。

下面的路線是從單塊架構開始,隨著架構師對業務域理解的不斷深入,也隨著業務和團隊規模的不斷擴大,漸進式地把單塊架構拆分成微服務架構的思路,這就是演化式架構的思維。如果你觀察現實世界中一些網際網路公司(例如eBay,阿里,Netflix等等)的系統架構,大部分走得都是演化式架構的路線。

下圖是建築的演化史,在每個階段,你可以看到設計的影子,但如果時間線拉得足夠長,演化的特性就出來了。

五、如何培養架構設計思維

良好的架構設計思維的培養,離不開工作中大量高質量項目的實戰鍛鍊,然後是平時的學習、思考和提煉總結。

另外,基本的架構設計思維,其實在我們大學計算機課程(比如數據結構和算法)中可以找到影子,只不過當時以學習為主,問題域比較小和理想化。所以大學教育其實非常重要,基本的架構設計思維在那個時候就已經埋下種子,後面工程實踐中進一步消化和應用,隨著經驗的積累,我們能夠解決的問題域複雜性和規模逐漸變大,但基本的武器還是抽象、分層和分治等思維。

我認為一個架構師的成長高度和他大學期間的思維習慣的養成關係密切。我所知道世界一流的網際網路公司,例如谷歌等,招聘工程師新人時,對數據結構和算法的要求可以用苛刻來形容,這個可以理解,谷歌級別公司要解決的問題都是超級複雜的,基本思維功底薄弱根本無法應對。

對於演化設計思維,當前的大學教育其實培養很少,相反,當前大學教育大都採用脫離現實場景的簡化理想模型,有些還是固定答案的應試教學,這種方式會造成學生思維確定化,不利於培養演化式設計思維。我個人的體會,演化式設計思維更多在實際工作中通過實戰鍛鍊和培養。

結論

  1. 架構的本質是管理複雜性,抽象、分層、分治和演化思維是架構師征服複雜性的四種根本性武器。
  2. 掌握了抽象、分層、分治和演化這四種基本的武器,你可以設計小到一個類,一個模塊,一個子系統,或者一個中型的系統,也可以大到一個公司的基礎平台架構,微服務架構,技術體系架構,甚至是組織架構,業務架構等等。
  3. 架構設計不是靜態的,而是動態演化的。只有能夠不斷應對環境變化的系統,才是有生命力的系統。所以即使你掌握了抽象、分層和分治這三種基本思維,仍然需要演化式思維,在設計的同時,藉助反饋和進化的力量推動架構的持續演進。
  4. 架構師在關注技術,開發應用的同時,需要定期梳理自己的架構設計思維,積累時間長了,你看待世界事物的方式會發生根本性變化,你會發現我們生活其中的世界,其實也是在抽象、分層、分治和演化的基礎上構建起來的。另外架構設計思維的形成,會對你的系統架構設計能力產生重大影響。可以說對抽象、分層、分治和演化掌握的深度和靈活應用的水平,直接決定架構師所能解決問題域的複雜性和規模大小,是區分普通應用型架構師和平台型/系統型架構師的一個分水嶺。

end:如果你覺得本文對你有幫助的話,記得關注點贊轉發,你的支持就是我更新動力。

文章來源: https://twgreatdaily.com/zh-sg/PVLxrG0BMH2_cNUgwL48.html