若是如今讓你闡述一下什麼是「分佈式系統」,你腦子裏第一下跳出來的是什麼?我想,此時能夠用蘇東坡先生的一句詩,來形象地描述你們對分佈式系統的認識:mysql
橫當作嶺側成峯,遠近高低各不一樣。sql
1「分佈式系統」等於 SOA、ESB、微服務這些東西嗎?數據庫
我以爲每一個人腦子裏一會兒涌現出來的確定是很是具象的東西,就像下面這些:緩存
「分佈式系統」等於 SOA、ESB、微服務這些東西嗎?架構
若是你一會兒想到的是 XX 中心、XX 服務,意味着你把服務化的模式(SOA、ESB、微服務)和分佈式系統錯誤地劃上了等號。併發
那麼,什麼是「服務化」呢?服務化就像企業當中將相同崗位的人員劃分到同一個部門管理,以此來收斂特定的工做入口,再進行二次分配,以提升人員利用率和勞動成果的複用度。服務化的本質是「分治」,而「分治」的前提是先要拆,而後才談得上如何治。這時,高內聚、低耦合的思想在拆分過程當中起到了一個很是重要的做用,由於這能夠儘量地下降拆分後不一樣組件間進行協做的複雜度。因此重要的是「怎麼拆「,還有如何按部就班地拆,而這個過程當中你到底是採用了何種服務化模式(好比 SOA、ESB、微服務等)並非關鍵。框架
爲何說「怎麼拆」最重要呢?我來舉個例子,企業的組織架構包括三種模型:職能型、項目型、矩陣型。你能夠把這裏的企業理解爲一個「分佈式系統」,把後面的 3 種模型理解爲這個分佈式系統的 3 種形態。做爲這個「系統」的全部人,你須要考慮如何拆分它,才能使得各功能組件相互之間能夠更好地協做。假設,你要將一個總計 10000 名員工的企業按「職能型」拆分紅 20 個部門,獲得的結果是每一個部門 500 人。異步
這時,若是工做是流水線式的上下游關係。一個部門完工了再交給下一個部門。分佈式
那麼這時候是高內聚、低耦合的。由於一個工種只與另外一個工種產生了關聯,而且僅有一次。函數
但若是工做須要頻繁的由不一樣職能的人員同時進行,會致使同一個部門可能與多個部門產生聯繫。
那麼,這時是低內聚、高耦合的。由於一個工種須要和其餘多個工種產生關聯而且遠不止一次。
能夠看到服務化體現了「分治」的效果,這也是分佈式系統的核心思想,所以從「分治」這個本質上來看,服務化的確是分佈式系統,但分佈式系統不只僅停留在那些服務化的模式上。
我相信,你在工做中參與開發的任何軟件系統,處處都存在着須要拆分的地方,除非它的功能極簡到只須要計算一個 1+1。好比,當咱們在電商平臺點擊「提交訂單」的時候,會涉及生成訂單、扣除積分、扣除庫存等等動做。電商系統初期全部的功能可能都在一個系統裏面,那麼這些操做能夠寫在一個方法體裏嗎?我想只要代碼可以成功運行,大部分人是不會管你怎麼寫的。可是若是這時須要增長一個紅包功能呢?相信你或多或少遇到過在幾百上千行代碼中去增改功能的事情,其中的痛苦應該深有體會。
要解決這個問題就是要作拆分,經過梳理、歸類,將不一樣的緊密相關的部分收斂到一個獨立的邏輯體中,這個邏輯體能夠是函數、類以及命名空間,等等。因此,從這個角度來講「分治」的問題其實早就存在咱們的工做中,就看咱們是否有去關注它了。所以,這並不僅是咱們在進行服務化時才須要考慮的問題。
那麼如何才能作好這個事情,更好的拆分能力正是咱們須要掌握的。若是隻是由於看到其餘人這麼拆,我也這麼拆,根據「二八原則」,或許「依樣畫葫蘆」能夠達到 80% 的契合度,可是每每那剩下的 20% 會是耗費咱們 80% 精力的「大麻煩」。要知道,只有掌握了核心主旨,才能更快地找到最理想的高內聚、低耦合方案。
2「分佈式系統」是各類中間件嗎?
又或許,聽到分佈式系統,你想到了某某 MQ 框架、某某 RPC 框架、某某 DAL 框架,把運用中間件和分佈式系統錯誤地劃上了等號。
這裏須要搞清楚的是,中間件起到的是標準化的做用。中間件只是承載這些標準化想法的介質、工具,能夠起到引導和約束的效果,以此起到大大下降系統複雜度和協做成本的做用。咱們來分別看一下:
MQ 框架標準化了不一樣應用程序間非實時異步通訊的方式。
RPC 框架標準化了不一樣應用程序間實時通信的方式。
DAL(Data Access Layer,數據訪問層)框架標準化了應用程序和數據庫之間通信的方式。
因此,雖然分佈式系統中會運用中間件,但分佈式系統卻不只僅停留在用了什麼中間件上。你須要清楚每一類中間件背後是對什麼進行了標準化,它的目的是什麼,帶來了哪些反作用,等等。只有如此,你才能真正識別不一樣技術框架之間的區別,找到真正適合當前系統的技術框架。
那麼標準是拍腦殼決定的嗎?確定不是,正如前面所說每一次標準化都是有目的的,須要產生價值。好比,大部分中間件都具有這樣一個價值:
爲了在軟件系統的迭代過程當中,避免將精力過多地花費在某個子功能下衆多差別不大的選項中。
在現實中,這點更多時候出如今技術層面的中間件裏,好比,數據庫訪問框架的做用是爲了標準化操做不一樣數據庫的差別,使得上層應用程序不用糾結於該怎麼與 mysql 交互或者該怎麼與 SQL SERVER 交互。由於與業務相比,技術層面「穩定」多了,因此作標準化更有價值,更能得到長期收益。但「穩定」是相對的,哪怕單純在業務層面也存在相對穩定的部分。
好比,你能夠想象一下「盛飯」的場景,在大多數狀況下其中相對穩定的是什麼,不穩定的是什麼。想完以後看下面的示例。
... 基類:人 繼承基類的子類:男人、女人 基類:碗 繼承基類的子類:大碗、小碗、湯碗 基類:勺子 繼承基類的子類:鐵勺、陶瓷勺、塑料勺 function 盛飯(參數 人,參數 碗,參數 勺子){ do 人拿起碗 do 人拿起勺子 do 人用勺子舀起飯 do 人把勺子放到碗的上方並倒下 } ...
從這個示例裏咱們發現,不穩定的部分都已經成爲變量了,那麼剩下的這個方法體起到的做用和前面提到的中間件是同樣的,它標準化,標準化了盛飯的過程。因此識別相對穩定的部分是什麼,如何把它們提煉出來,而且圍繞這些點進行標準化,纔是咱們須要掌握的能力。而鍛鍊這個能力和須要這個能力的地方一樣並不侷限於分佈式系統。
列舉這些現象只是想說,咱們在認知一個分佈式系統的時候,內在勝於表象,掌握一個紮實的理論基本功更爲重要。並且,這些訓練場無處不在。
3海市蜃樓般的「分佈式系統」
我相信,自從進入移動時代以來,各類高大上的系統架構圖愈來愈頻繁地出現,你的眼前充斥着各類主流、非主流的眼花繚亂的技術框架。你不禁得肅然起敬一番,心中吶喊着:「對,這就是我想去的地方,我想參與甚至實現一個這樣牛逼的分佈式系統,不再想天天只是增刪改查了。」
得不到的事物老是美好的,但每每咱們也會過分地高估它的美好。與此相似,高大上的架構圖背後呈現的系統的確也是一個成熟分佈式系統的樣貌,但咱們要清楚一點:羅馬不是一日建成的。
並且,「分佈式」這個詞只是意味着形態上是散列狀的,而「一分爲二」和「一分爲 N」本質上並無區別。因此,不少小項目或者大型項目的初期所搭配的基礎套餐「單程序 + 單數據庫」,一樣能夠理解爲分佈式系統,其中遇到的問題不少一樣也存在於成熟的分佈式系統中。
想象一下,下面的場景是否在「單程序 + 單數據庫」項目中出現過?
log 記錄執行成功,可是數據庫的數據沒發生變化;
進程內的緩存數據更新了,可是數據庫更新失敗了。
這裏咱們停頓 30 秒,思考一下爲何會出現這些問題?
這裏須要咱們先思考一下「軟件」是什麼。 軟件的本質是一套代碼,而代碼只是一段文字,除了提供文字所表述的信息以外,自己沒法「動」起來。可是,想讓它「動」起來,使其可以完成一件咱們指定的事情,前提是須要一個宿主來給予它生命。這個宿主就是計算機,它可讓代碼變成一連串可執行的「動做」,而後經過數據這個「燃料」的觸發,「動」起來。這個持續的活動過程,又被描述爲一個運行中的「進程」。
那麼除了咱們開發的系統是軟件,數據庫也是軟件,前者負責運算,後者負責存儲運算後的結果(也可稱爲「狀態」),分工協做。
因此,「單程序 + 單數據庫」爲何也是分佈式系統這個問題就很明白了。由於咱們所編寫的程序運行時所在的進程,和程序中使用到的數據庫所在的進程,並非同一個。也所以致使了,讓這兩個進程(系統)完成各自的部分,然後最終完成一件完整的事,變得再也不像由單個個體獨自完成這件事那麼簡單。這就如「兩人三足」遊戲同樣,如何儘量地讓外部看起來像是一個總體、天然地前進。
因此,咱們能夠這麼理解,涉及多個進程協做才能提供一個完整功能的系統就是「分佈式系統」。
那麼再回到上面舉例的兩個場景,咱們在思考「單程序 + 單數據庫」項目中遇到的這些問題背後的緣由和解決它的過程時,與咱們在一個成熟的分佈式系統中的遭遇是同樣的,例如數據一致性。固然,這只是分佈式系統核心概念的冰山一角。
維基百科對「分佈式系統」的宏觀定義是這樣的:
分佈式系統是一種其組件位於不一樣的聯網計算機上的系統,而後經過互相傳遞消息來進行通訊和協調。爲了達到共同的目標,這些組件會相互做用。
咱們能夠再以大小關係來解釋它:把須要進行大量計算的工程數據分割成小塊,由多臺計算機分別計算,而後將結果統一合併得出數據結論的科學。這本質上就是「分治」。而「單程序 + 單數據庫」組合的系統也包含了至少兩個進程,「麻雀雖小五臟俱全」,這也是「分佈式系統」。
4總結
如今,咱們搞清楚了,看待一個「分佈式系統」的時候,內在勝於表象。以及,只要涉及多個進程協做才能提供一個完整功能的系統,就是「分佈式系統」。
我相信還有不少其餘景象出現你的腦海中,但這大多數都是分佈式系統的本質產生的「化學反應」,進而造成的結果。若是停留在這些表象上,那麼咱們最終將沒法尋找到「分佈式系統」的本質,也就沒法獲得真正的「道」,更不會真正具有駕馭這些形態萬千的「分佈式系統」的能力。
因此,但願你在學習分佈式系統的時候,不要因追逐「術」而丟了「道」。沒有「道」只有「術」是空殼,最終會走火入魔,學得越多,會越混亂,處處都是矛盾和疑惑。
所以,咱們這個系列除了教給你在具體場景下的最佳實踐,還會和你講解爲何這樣作,以及該如何去權衡不一樣方案。不會過多的講述具體的技術框架,大部份內容圍繞理論展開,欲使每一個人可以掌握好這些分佈式中的基礎理論和思路,修煉好本身的內功。
我將在後續的文章中,以一個項目的初期到成熟期做爲路線圖,帶領你按部就班地深刻到分佈式系統中,層層遞進地去剝開它的本質,而且圍繞這個本質去思考(是什麼問題,有哪些方式能夠解決,何時該用何種種方式等等),讓你知其然且知其因此然,造成一套完整的知識體系,完成核心「骨架」的塑造。而在此以後,你本身在課外學習時,就能夠去填充「血肉」部分,逐漸豐滿本身。將來,你們的區別就在於胖一點和瘦一點,但只要能很好地完成工做,胖瘦又有何影響?
站在全局角度看,分佈式系統的本質是什麼?其實說白了,就是兩點:「分治」和「冗餘」。分治和冗餘使得分佈式系統具有了核心價值,那麼它的價值是什麼?
上一篇中,咱們從整體上聊了聊分佈式系統:多是講分佈式系統最到位的一篇文章。這一篇中,咱們將聊聊分佈式系統的本質。
分佈式系統的價值
談到分佈式系統的價值,可能就得從 1953 年提及了。在這一年,埃布·格羅希(Herb Grosch)提出了一個他觀察得出的規律——Grosch 定律。維基百科中是這樣描述的:
計算機性能隨着成本的平方而增長。若是計算機 A 的成本是計算機 B 的兩倍,那麼計算機 A 的速度應該是計算機 B 的四倍。
這一論斷與當時的大型機技術很是吻合,於是使得許多機構都盡其所能購買最大的單個大型機。其實,這也很是符合慣性思惟,簡單粗暴。
然而,1965 年高登·摩爾(Gordon Moore)提出了摩爾定律。通過幾年的發展,人們發現摩爾定律的預測是符合現實的。這就意味着,集中式系統的運算能力每隔一段時間才能提高一倍。
那麼,到底要隔多久呢?這個「時間」有不少版本,好比廣爲流傳的 18 個月版本,以及 Gordon Moore 本人堅持的 2 年版本。這裏咱們不用太過糾結於實際狀況究竟是哪一個「時間」版本,由於這其中隱含的意思更重要,即:若是你的系統需承載的計算量的增加速度大於摩爾定律的預測,那麼在將來的某一個時間點,集中式系統將沒法承載你所需的計算量。
而這只是一個內在因素,真正推進分佈式系統發展的催化劑是「經濟」因素。
人們發現,用廉價機器的集合組成的分佈式系統,除了能夠得到超過 CPU 發展速度的性能外,花費更低,具備更好的性價比,而且還能夠根據須要增長或者減小所需機器的數量。
因此,咱們獲得一個新結論:不管是要以低價格得到普通的性能,仍是要以較高的價格得到極高的性能,分佈式系統都可以知足。而且受規模效應的影響,系統越大,性價比帶來的收益越高。
以後,進入到互聯網快速發展的時期,咱們看到了分佈式系統相比集中式系統的另外一個更明顯的優點:更高的可用性。例如,有 10 個可以承載 10000 流量的相同的節點,若是其中的 2 個掛了,只要實際流量不超過 8000,系統依然可以正常運轉。
而這一切的價值,都是創建在分佈式系統的「分治」和「冗餘」之上的。
分 治
分治,字面意思是「分而治之」,和咱們的大腦在解決問題時的思考方式是同樣的。咱們能夠將整個過程分爲 3 步:分解 -> 治理 -> 歸併。而分治思想的表現形式多樣,分層、分塊都是它的體現。
這麼作的好處是:問題越小越容易被解決,而且,只要解決了全部子問題,父問題就均可以被解決了。可是,這麼作的時候,須要知足一個最重要的條件:不一樣分支上的子問題,不能相互依賴,須要各自獨立。由於一旦包含了依賴關係,子問題和父問題之間就失去了能夠被「歸併」的意義。在軟件開發領域,咱們把這個概念稱爲「耦合度」和「內聚度」,這兩個度量概念很是重要。
耦合度,指的是軟件模塊之間相互依賴的程度。好比,每次調用方法 A 以後都須要同步調用方法 B,那麼此時方法 A 和 B 間的耦合度是高的。
內聚度,指的是模塊內的元素具備的共同點的類似程度。好比,一個類中的多個方法有不少的共同之處,都是作支付相關的處理,那麼這個類的內聚度是高的。
內聚度一般與耦合度造成對比。低耦合一般與高內聚相關,反之亦然。
因此,當你打算進行分治的時候,耦合度和內聚度就是須要考慮的重點。
下面咱們來看個例子,體會一下耦合度和內聚度的含義。(圖僅用於表達含義,切勿做其餘參考)
假設一個電商平臺,爲了應對更大的訪問量,須要拆分一個同時包含商品、促銷的系統。若是垂直拆分,是這樣:
而若是水平拆分,則是這樣的:
假如咱們面對的場景僅僅是具體的商品詳情展現頁面,很顯然,用水平拆分的效果會更好。由於傳統的商品展現必然會同時展現促銷,因此,若是用水平拆分,一次請求便可獲取全部數據,內聚度很是高,而且此時模塊間徹底沒有耦合。而若是是垂直拆分的話,就須要同時請求 2 個節點的數據並進行組合,所以耦合度更高、內聚度更差。
可是,這樣的假設在真實的電商場景中是不存在的。從全局來看,訂單、購物車、商品列表等許多其餘場景也須要促銷信息。而且這個時候咱們發現引入了一些新的主體,諸如訂單、購物車、商品分類等等。這個時候,水平拆分帶來的好處愈來愈小,由於這樣只解決了多個耦合中的一個,低耦合喪失了。而且隨着商品和促銷與外界的關聯愈來愈多,必然有些場景僅僅涉及到商品和促銷的其中一個,可是處理的時候,咱們還須要避免受到另外一個的影響。如此,高內聚也喪失了。
這個時候,反而經過垂直拆分能夠得到更優的耦合度和內聚度,以下圖。
這個時候,最高的耦合關係從原先的 6 降到了 4,而且商品和促銷各自的處理相互不受影響。
因此,你會發現隨着業務的變化,耦合度與內聚度也會發生變化。所以,及時地進行梳理和調整,能夠避免系統的複雜度快速增加,才能最大程度的發揮「分治」帶來的好處。
綜上,分治能夠簡化解題的難度,經過高內聚、低耦合的協做關係達到更好「性能與經濟比」,來承載更大的流量。而「冗餘」則帶來了系統能夠 7*24 小時不間斷運做的但願。
冗 餘
這裏的冗餘並不等同於代碼的冗餘、無心義的重複勞動,而是咱們有意去作的、人爲增長的重複部分。其目的是允許在必定範圍內出現故障,而系統不受影響,以下圖。
此時,咱們能夠將冗餘的節點部署在一個獨立的環境中。這個獨立的環境,多是處於同一個局域網內的不一樣主機,也多是在不一樣的局域網,還多是在不一樣的機房。很顯然,它們可以應對的故障範圍是逐步遞增的。
可是,像這種單純地爲了備用而作的冗餘,最大的弊端是,若是沒有出現故障,那麼冗餘的這部分資源就白白浪費了,不能發揮任何做用。因此,咱們才提出了諸如雙主多活、讀寫分離之類的概念,以提升資源利用率。
固然,除了軟件層面,硬件層面的冗餘也是一樣的道理。好比,磁盤陣列能夠容忍幾塊以內磁盤損壞,而不會影響總體。
不過也很顯然,當故障影響範圍大於你冗餘的容量時,系統依然會掛。因此,既然你沒法預知故障的發生狀況,那麼作冗餘的時候須要平衡的另外一端就是成本。相比更多的冗餘,追求更好的性價比更合理一些。
在咱們生活中的冗餘也處處存在。好比,大部分的飛機和直升機的發動機都是偶數的,汽車中的電子控制系統的冗餘機制等。就比如替身與真身的關係,冗餘的就是替身。它能夠和真身同時活動,也能夠代替真身活動。
分治和冗餘講究的都是分散化,最終造成一個完整的系統還須要將它們「鏈接」起來。天下沒有免費的午飯,得到分佈式系統價值的同時,這個「再鏈接」的過程就是咱們相比集中式系統要作的額外工做。
再鏈接
如何將拆分後的各個節點再次鏈接起來,從模式上來講,主要是去中心化與中心化之分。
前者徹底消除了中心節點故障帶來的全盤出錯的風險,卻帶來了更高的節點間協做成本。後者經過中心節點的集中式管理大大下降了協做成本,可是一旦中心節點故障則全盤出錯。
另外,從技術角度來講,如何選擇通訊協議和序列化機制,也是很是重要的。
雖然不少通信協議和序列化機制徹底能夠承擔任何場景的鏈接責任,可是不一樣的協議和序列化機制在適合的場景下才能發揮它最大的優點。好比,須要更高性能的場景運用 TCP 協議優於 HTTP 協議;須要更高吞吐量的場景運用 UDP 協議優於 TCP 協議,等等。
總 結
無論系統的規模發展到多大,合理地拆分,加上合適的鏈接方式,那麼至少會是一個運轉順暢、協做舒服的系統,至少可以正常發揮分佈式系統應有的價值。
現在,咱們發現分佈式系統還能夠發揮更多的做用。
好比,只要基於一個統一的上層通訊協議,其下層的不一樣節點能夠運用不一樣的技術棧來發揮不一樣技術各自的優點,好比用 Go 來應對高併發場景,用 Python 來作數據分析等。
再好比,提升交付的速度,以下圖。
經過分配不一樣的團隊、人員同時進行多個模塊的開發,雖然總的耗時增長了,可是總體的交付速度加快了。
事物最本質的東西是恆定的、不變的,能夠指引咱們的工做方向。分佈式系統的本質也是這樣。例如,這樣的「分治」方案耦合度和內聚度是否最優,這樣作「冗餘」帶來的收益是否成本可以接受。只要持續帶着這些思考,咱們就好像拿着一杆秤,基於它,咱們就能夠去衡量各類變量影響,而後做權衡。好比成本、時間、人員、性能、易維護等等。也能夠基於它去判斷什麼樣的框架、組件、協議更適合當前的環境。
須要不斷的權衡,也意味着分佈式系統的設計工做必定不是一步到位,而是按部就班的。由於過度爲未知的將來作更多的考量,最終可能都會打水漂。因此,建議以多考慮 1~2 步爲宜。假如以你所在的團隊中對重大技術升級的頻率來做爲參考的話,作可供 2 個升級週期的設計,花一個升級週期的時間先實現第一階段,下個階段能夠選擇直接實現剩下部分,也可繼續進行 2 個升級週期設計,開啓一個循環,持續迭代,而且不斷修正方向以更貼近現實的發展,就以下圖這樣。
在你的工做或者學習中,以爲分佈式系統還具有哪些價值呢?能夠在下方評論區留言。