從分層架構到微服務架構(一)

《從分層架構到微服務架構》是一系列介紹《Fundamentals of Software Architecture》中提到的 8 種架構模式的文章,這裏不會事無鉅細地介紹所有的細節,而是會挑選其中關鍵內容,更多詳情請閱讀原書。

前言

談到軟件系統設計的方法論,在代碼層面,有我們熟悉的 23 種設計模式(design pattern),對應到架構層面,則有所謂的架構模式(architecture pattern)。它們分別從微觀和宏觀的角度指導着我們設計出良好的軟件系統,因此,作爲一個軟件工程師,我們不僅要熟悉設計模式,對常見的架構模式也要熟稔於心。正如看到一個設計模式的名字腦裏就能浮現出大致的結構圖,當我們看到一個架構模式的名字時,也要馬上想到對應的架構圖及其基本特點。比如,當談到分層架構時,我們就應該想起它的架構圖是怎樣的、有哪些出色的架構特徵(architecture characteristics)、系統是如何部署的、數據存儲的策略是哪種、等等。

一般地,架構模式大致可以分成兩類,單體架構(monolithic architecture)和分佈式架構(distributed architecture)。本系列文章將會介紹以下 8 種常用的架構模式:

單體架構

分佈式架構

軟件設計中的謬誤

在介紹架構模式前,我們先談談軟件設計中的謬誤(fallacy)。所謂謬誤,就是在設計軟件系統,特別是分佈式系統時,我們先入爲主地假設它們是正確,但實際上並非如此的一些觀念。這些觀念都是我們在設計軟件時考慮不周的體現。

謬誤 1:網絡是可靠的

網絡是不可靠的

很多軟件工程師常常假設網絡是可靠的,但實際並非如此。相比 20 年前,現在的網絡會可靠很多,但是仍然具有很大的不確定性。如上圖所述,Serivce B 可能完全是正常運行的,但是因爲網絡的問題,Service A 發出的請求無法到達 Service B。一種更糟糕的場景是,Service B 可以收到 Service A 的請求,並處理了相關的數據,但是網絡問題導致了 Service A 無法收到 Service B 的響應,從而造成了數據不一致。網絡的不可靠也是爲什麼系統中常常出現服務通信超時、服務熔斷等的原因。

總而言之,如果假設網絡是可靠的,那麼我們設計出來的軟件系統將會是不可靠的。

謬誤 2:時延是 0

時延不爲 0

如上圖所示,服務內組件間的函數 / 方法級別的調用,耗時是微妙,甚至是納秒級別;但是服務間的遠程調用(比如 REST、消息隊列、RPC),耗時會是微秒級別,甚至在異常場景會達到了秒級!在設計系統,特別是分佈式系統時,時延是一個無法被忽視的因素,我們必須清楚系統的平均時延,否則設計出來的方案可能根本不可行。比如,假設系統中服務間通信時延爲 100ms,如果一個請求的調用鏈涉及到 10 個服務,那麼該請求的時延將會是 1000ms!這麼高的平均時延對於一般系統來說是完全無法接受的。

進行系統設計時,考慮平均時延還不夠,更重要的是 95th 和 99th 百分點。一個系統的平均時延可能僅僅只有數十毫秒,但是 95th 百分點的時延卻達到了數百毫秒,很多時候,這也恰恰成爲了拖垮整系統性能的那塊 “短板”。

謬誤 3:帶寬是無限的

帶寬是有限的

在單體架構中,業務流程都在單服務內閉環,消耗的帶寬很少甚至爲 0,因此帶寬並不是主要關注點。一旦將系統拆分成分佈式架構,一個業務流程可能涉及多個服務間的通信,帶寬就成了必須考慮的因素。帶寬的不足,會導致網絡變慢,從而影響系統的時延(謬誤 2:時延是 0)和可靠性(謬誤 1:網絡是可靠的)

如上圖所示,假設在一個 Web 系統中,Service A 負責處理前端請求,Service B 負責管理用戶信息(包括姓名、性別、年齡等 45 個屬性)。Service A 每處理一個請求都需要向 Service B 查詢用戶姓名(200 bytes),而在一次請求中,Service B 卻返回了用戶的所有信息(500 kb)。如果系統每秒處理 2000 次請求,每次請求消耗 500 kb 帶寬,那麼每秒消耗的總帶寬會是 1 Gb!如果 Service B 僅僅返回必須的姓名,那麼同等條件下,每秒消耗的總帶寬僅僅是 400 kb。

此類問題就是所謂的 stamp coupling,解決方法也很多,比如在請求中添加屬性選擇,使用 GraphQL 替代 REST。相比於這些技術手段,更重要的是確定服務間通信所需的最小數據集,並在進行系統設計時將其作爲一個重點關注的因素

謬誤 4:網絡是安全的

網絡是不安全的

VPN、防火牆等的廣泛使用,使得很多工程師在設計系統時忽略了 “網絡是不安全的” 這一重要原則。特別是從單體架構演進到分佈式架構以後,系統被攻擊的概率將會大大增加。因此,在分佈式系統中,每個服務都必須是安全的 endpoint,這樣才能確保任何未知或惡意的請求都被攔截掉。當然,安全是有代價的,這也是像微服務架構這類細服務粒度的系統,一次業務請求中調用鏈過長後性能極速下降的重要原因。

謬誤 5:網絡拓撲一成不變

網絡拓撲是時常變化的

這裏的網絡拓撲指的是系統運行時所涉及到的網絡設備,包括所有的路由器、防火牆、集線器、交換機等。很多工程師會假設網絡拓撲是固定的,然而並非如此。

假設如下場景,爲架構師的你在週一早上回到公司後,發現組內同事都在爲系統中所有的服務間通信都在不斷出現響應超時現象而抓狂,但奇怪的是週末並沒有做服務變更。經過幾個小時的攻關後,你發現週一凌晨 2 點時有過一次網絡升級,而恰恰是這次 “次要” 的網絡升級,推翻之前設計系統時的時延假設,從而觸發了本次事故。

因此,軟件工程師也需要與網絡管理員時常聯繫,確保在每次網絡升級前都明確網絡拓撲的變更點,從而做出相應的調整

謬誤 6:只有一個網絡管理員

不只有一個網絡管理員

網絡管理員往往不止有一個,特別是在 “雲” 時代,數據中心分散在多個地域,理所當然也存在着多個局域網。運行在 “雲” 上的系統很有可能跨越多個數據中心,因此工程師們應當感知各個數據中心的網絡管理員對網絡的相關操作,提前做出應對措施,避免出現因網絡拓撲變更(謬誤 5:網絡拓撲一成不變)而導致的服務通信超時,甚至觸發服務熔斷。

謬誤 7:通信成本爲 0

通信成本不爲 0

這裏的通信成本並非指網絡時延,而是指每增加一次服務間調用所導致的的花銷。很多工程師在設計系統時常常忽視掉通信成本,大家都在鼓吹分佈式架構相對了單體架構的優越性,卻忘記了它帶來的服務器、防火牆、網關等硬件的數量增加,這些都是白花花的銀子。

因此,在進行系統設計時,我們也應該將硬件資源和網絡拓撲納入考慮因素。

謬誤 8:網絡是同質的

網絡並非同質的

很多工程師都會假設網絡是同質的,也就是所有的網絡設備都來自同一硬件廠商,這當然也是一個謬誤。實際上,一個大的通信網絡中,硬件設備往往來自於不同的廠商,這得益於網絡協議標準的統一。廠商間設備的協作測試畢竟不會太充分,在一些特殊場景下極有可能存在網絡丟包,從而影響了網絡的可靠性(謬誤 1:網絡是可靠的)、時延(謬誤 2:時延是 0)以及帶寬(謬誤 3:帶寬是無限的)。

一切從 “大泥球” 開始

“大泥球” 架構

“大泥球” 架構是著名的反模式架構,最初在 1997 年由 Brian Foote 和 Joseph Yoder 提出。在 “大泥球” 架構裏,系統沒有進行內部的模塊劃分,代碼耦合嚴重,調用關係混亂,就像一個大的泥球。如上圖所示,每一個點代表一個類,紅線則表示類之間的耦合關係。這樣的架構對需求變更極不友好,往往牽一髮而動全身,而且在部署、可測試性、性能等方面也存在着很多問題。所有的架構師都在極力避免 “大泥球” 的出現,但很不幸的是,它仍然在實際項目中很常見,特別是項目伊始,代碼質量和結構還沒被嚴格管控起來前。

有反模式的出現,必然就有解決它的方法,這便是架構模式,從下一篇文章開始,我們將逐個介紹常見的 8 種架構模式。

總結

跟設計模式類似,架構模式是軟件工程師們多年來在架構設計方面的經驗總結。每種架構模式並沒有絕對的優劣之分,我們不能說微服務架構就一定比單體分層架構優越,它們都有着各自的應用場景。分佈式架構比單體架構有着更好的可擴展性、容錯性,但也帶來了更高的複雜性,比如分佈式事務。因此,我們應該熟知各個架構模式的特點,這樣才能在特定的業務場景使用合適的架構模式。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/6BvsfzDKeVI7_DyNj5gPvA