工作多年,如何從 CRUD Boy 轉型為分布式系統架構師?解鎖分布式系統的藝術:從零開始理解分布式系統架構與設計原理!
65 哥已經工作5年了,一直做著簡單重復的編程工作,活活熬成了一個只會 CRUD 的打工 boy。
65 哥:總是聽大佬講分布式分布式,什么才是分布式系統呢?
分布式系統是一個硬件或軟件系統分布在不同的網絡計算機上,彼此之間僅僅通過消息傳遞進行通信和協調的系統。
在一個分布式系統中,一組獨立的計算機展現給用戶的是一個統一的整體,就好像是一個系統似的。
系統擁有多種通用的物理和邏輯資源,可以動態(tài)的分配任務,分散的物理和邏輯資源通過計算機網絡實現信息交換。
65 哥:巴拉巴拉,能不能講點人話。俺聽不懂。
那好,下面我們從平常最熟悉的事物開始理解分布式系統如何出現,發(fā)展的,并經過實踐總結通用的理論,這些理論成為指導我們如何設計更完善的分布式系統的基礎。
從此篇文章,你將學習到以下知識:
圖片
Web 應用的擴展
為什么會出現分布式應用?
65 哥:這個我也不清楚啊,以前我寫的 web 應用都是直接扔進 Tomcat 中,啟動 Tomcat 就可以訪問了,這肯定不是分布式應用。
圖片
嗯,我們就從大家最熟悉 web 后臺應用講起,以前我們的系統訪問量小,業(yè)務也不復雜,一臺服務器一個應用就可以處理所有的業(yè)務請求了,后來我們公司發(fā)達了,訪問量上去了,業(yè)務也拓展了,雖然老板依舊沒有給我們加工資,確總是埋怨我們系統不穩(wěn)定,扛不住大并發(fā),是可忍孰不可也,加錢,我們要升級。
如果我們的服務器可以無限添加配置,那么一切性能問題都不是問題。
為提高系統處理能力,我們首先想到的擴展方式就是升級系統配置,8 核 cpu 升級為 32 核,64 核,內存 64G 升級為 128G,256G,帶寬上萬兆,十萬兆,這就叫做垂直擴展。
但這樣的擴展終將無法持續(xù)下去,原因如下。
- 單機系統的處理能力最終會達到瓶頸
- 單機升級的邊際成本將越來越大
worth
沒有什么可以攔住我們編程打工人的步伐。
俗話說,系統撐不住了,就加服務器,一臺不行就加兩臺。
當垂直擴展到達技術瓶頸或投入產出比超過預期,我們可以考慮通過增加服務器數量來提高并發(fā)能力,這種方式就是水平擴展。
圖片
系統拆分
65 哥:哦,這就是分布式系統了?這么簡單的么。
我勒個呵呵,哪有那么簡單,在水平擴展中,我們增加了服務器數量,但是如何讓這些服務器像一個整體一樣對外提供穩(wěn)定有效的服務才是關鍵。
既然已經有了多臺服務器,我們就要考慮如何將系統部署到到不同的節(jié)點上去。
65 哥:這還不簡單,我將我的 SpringBoot 項目部署到多臺服務器上,前面加個 nginx 就可以了,現在我們的系統都是這樣的,穩(wěn)定高效 perfect。給你畫個架構圖(小聲,這個我在學校時就會了。)
圖片
哪有什么歲月靜好,只不過是有人在為你負重前行。上面你所認為的簡單,其實有兩個原因:
- 系統分離不徹底,很重要的一點,就是依然在共享一個數據庫
- 在這個成熟的體系中,有太多成熟的中間件在為我們服務,比如上面提到的 nginx
系統拆分也有兩種方式,垂直拆分和水平拆分,注意,這里和上面提到的垂直擴展和水平擴展不是處理同一個問題的。(65 哥:哈哈,我知道,世間萬物不外乎縱橫二字)。
系統的垂直拆分,就是將相同的系統部署多套,所有的節(jié)點并沒有任何不同,角色和功能都一樣,它們各自分擔一部分功能請求,這樣整個系統的處理能力的上升了。
從處理 web 請求上來看,垂直拆分的每個節(jié)點都處理一個完整的請求,每個節(jié)點都承擔一部分請求量;
從數據存儲的角度看,每個數據節(jié)點都存儲相同的業(yè)務數據,每個節(jié)點存儲一部分數據。
圖片
系統的水平拆分,就是將系統按不同模塊或角色拆分,不同的模塊處理不同的事情。
從 web 請求上來看,需要多個相互依賴的系統配合完成一個請求,每個節(jié)點處理的需求不一致;
從數據存儲角度上來看,每個數據節(jié)點都存儲著各自業(yè)務模塊相關的數據,它們的數據都不一樣。
圖片
上面垂直拆分之后各個節(jié)點組成的就是一個集群,而水平拆分各個節(jié)點就是分布式。
這就是集群和分布式的區(qū)別。
集群除了上面提到的可以提高并發(fā)處理能力外,還可以保證系統的高可用,當一部分節(jié)點失效后,整個系統依舊可以提供完整的服務。分布式也一樣,除了提高并發(fā)能力,解耦系統,使系統邊界更清晰,系統功能更內聚也是其一大好處,所以在實際的系統中我們往往這兩種方式同時都在使用,而且我們常常提及的分布式系統其實是包含著集群的概念在里面的。
split
分布式目標
歸納和演繹是人類理性的基石,學習和思考就是不斷的歸納過去的經驗,從而得到普遍的規(guī)律,然后將得之的規(guī)律演繹于其他事物,用于指導更好的實踐過程。
圖片
上面我們講解了分布式系統的由來,現在我們回顧和總結一下這個過程。我們引入分布式系統必然是基于現實的需求和目標而來的。
65 哥:那分布式的目標什么呢?
分布式就是為了滿足以下目標而設計的:
- Transparency: 透明性,即用戶是不關心系統背后的分布式的,無論系統是分布式的還是單機的,對用戶來說都應該是透明的,用戶只需要關心系統可用的能力。這里的透明性就包括以下方面:
訪問透明性:固定統一的訪問接口和方式,不因為分布式系統內部的變動而改變系統的訪問方式。
位置透明性:外部訪問者不需要知道分布式系統具體的地址,系統節(jié)點的變動也不會影響其功能。
并發(fā)透明性:幾個進程能并發(fā)的使用共享資源而不互相干擾。
復制透明性:使用資源的多個實例提升可靠性和性能,而用戶和程序員無需知道副本的相關信息。
故障透明性:分布式系統內部部分節(jié)點的故障不影響系統的整體功能。
移動透明性:資源和客戶能夠在系統內移動而不受影響。
性能透明性:負載變化時,系統能夠被重新配置以提高性能。
伸縮透明性:系統和應用能夠進行擴展而不改變系統結構和應用算法。
- Openness: 開放性,通用的協議和使用方式。
- Scalability: 可伸縮性,隨著資源數量的增加和用戶訪問的增加,系統仍然能保持其有效性,該系統就被稱為可伸縮的。分布式系統應該在系統大小,系統管理方面都可擴展。
- Performance: 性能,相對于單體應用,分布式系統應該用更加突出的性能。
- Reliability: 可靠性,與單體系統相比,分布式系統應具有更好安全性,一致性和掩蓋錯誤的能力。
分布式挑戰(zhàn)
分布式的挑戰(zhàn)來源于不確定性。想一想,分布式系統相對于單體應用,多了哪些東西?
65 哥:有了更多的服務節(jié)點,還有就是服務之間的網絡通信。
是的,看來 65 哥同學已經懂得思考和分析系統了。分布式系統的所有挑戰(zhàn)就來源于這兩者的不確定性。
- 節(jié)點故障:
節(jié)點數量越多,出故障的概率就變高了。分布式系統需要保證故障發(fā)生的時候,系統仍然是可用的,這就需要系統能夠感知所有節(jié)點的服務狀態(tài),在節(jié)點發(fā)生故障的情況下將該節(jié)點負責的計算、存儲任務轉移到其他節(jié)點。
- 不可靠的網絡:
節(jié)點間通過網絡通信,我們都知道網絡是不可靠的??赡艿木W絡問題包括:網絡分割、延時、丟包、亂序。
相比單機過程調用,網絡通信最讓人頭疼的是超時已經雙向通行的不確定性。出現超時狀態(tài)時,網絡通信發(fā)起方是無法確定當前請求是否被成功處理的。
在不可靠的網絡和節(jié)點中,分布式系統依然要保證其可用,穩(wěn)定,高效,這是一個系統最基本的要求。因此分布式系統的設計和架構充滿了挑戰(zhàn)。
分而治之
分布式系統就是充分利用更多的資源進行并行運算和存儲來提升系統的性能,這就是分而治之的原理。
65 哥:哦,懂了懂了,那 MapReduce 的 map,Elasticsearch 的 sharding,Kafka 的 partition 是不是都是分布式的分而治之原理。
可以啊,65 哥同學不僅能夠歸納,還能夠舉一反三了。
不錯,無論是 map,sharding 還是 partition,甚至 請求路由負載均衡 都是在將計算或數據拆分,再分布到不同的節(jié)點計算和存儲,從而提高系統的并發(fā)性。
不同集群類型的拆分方式
sharding
同樣是分,在不同領域的,甚至不同實現的系統中通常會有不同的說法。
sharding 通常是在數據存儲系統中將不同數據分布到不同節(jié)點的方式,中文通常翻譯為數據分片。
比如在 MongoDB 中,當 MongoDB 存儲海量的數據時,一臺機器可能不足以存儲數據,也可能不足以提供可接受的讀寫吞吐量。
這時,我們就可以通過在多臺機器上分割數據,使得數據庫系統能存儲和處理更多的數據。
圖片
比如在 Elasticsearch 中,每個索引有一個或多個分片,索引的數據被分配到各個分片上,相當于一桶水用了 N 個杯子裝。分片有助于橫向擴展,N 個分片會被盡可能平均地(rebalance)分配在不同的節(jié)點上。
圖片
partition
partition的概念經常在 Kafka 中可以看到,在 kafka 中 topic 是一個邏輯概念,從分布式隊列的角度看,topic 對使用者來說就是一個隊列,topic 在 kafka 的具體實現中,由分布在不同節(jié)點上的 partition 組成,每個 partition 就是根據分區(qū)算法拆分的多個分區(qū),在 kafka 中,同一個分區(qū)不能被同一個 group 下的多個 consumer 消費,所以一個 topic 有多少 partition 在一定意義上就表示這個 topic 具有多少并發(fā)處理能力。
圖片
在 Amazing 的分布式數據庫DynamoDB中,一張表在底層實現中也被分區(qū)為不同的 partition。
load balance
負載均衡是高可用網絡基礎架構的關鍵組件,通常用于將工作負載分布到多個服務器來提高網站、應用、數據庫或其他服務的性能和可靠性。
比如 nginx 的負載均衡,通過不同的負載均衡分配策略,將 http 請求分發(fā)到 web 應用的不同節(jié)點之上,從而提高應用的并發(fā)處理能力。
比如 dubbo 的客戶端負載能力,可以將 dubbo 請求路由到具體的 producer 提供節(jié)點上,負載均衡是一個完善的 RPC 所應該具有的能力。
在 Spring Cloud 的體系中 Robbin 組件可以通過 Spring Cloud 的各微服務之間通信的負載均衡分配問題,依舊是將請求分發(fā)到集群中的不同節(jié)點上去。
分的策略
無論是分區(qū)還是分片,還是分區(qū)路由,其實都有一些通用的分區(qū)算法,以下的概念可能很多同學都在不同的領域看到過,如上面看到的反向代理服務器 nginx 中,如分布式消息隊列 kafka 中,如 RPC 框架 Dubbo 中,這有時候會讓很多同學感到懵。
其實無論在什么領域中,你只要抓住它在完成的核心功能上就可以理解,它們就是在考慮如何分的問題,把處理請求(即計算)如何均勻地分到不同的機器上,把數據如何分配到不同的節(jié)點上。
從大的方向看分有兩種策略,一種可復刻,一種不可復刻。
可復刻,這種策略根據一定算法分配計算和數據,在相同的條件下,無論什么時間點得出的結果相同,因此對于相同條件的請求和數據來說是可復刻的,在不同時間點相同的請求和數據始終都在統一節(jié)點上。這種策略一般用于有數據狀態(tài)在情況。
不可復刻,這種策略使用全隨機方式,即使在相同的條件下,不同時間點得出的結果也不一致,因此也是不可還原的,如果只是為了可還原,如果通過元數據記錄已經分配好的數據,之后需要還原時通過元數據就可以準確的得知數據所在位置了。
65 哥:這么神奇么?我想看看不同系統都有什么策略。
Dubbo 的負載均衡
Dubbo 是阿里開源的分布式服務框架。其實現了多種負載均衡策略。
圖片
Random LoadBalance
隨機,可以按權重設置隨機概率。在一個截面上碰撞的概率高,但調用量越大分布越均勻,而且按概率使用權重后也比較均勻,有利于動態(tài)調整提供者權重。
RoundRobin LoadBalance
輪詢,按公約后的權重設置輪詢比率。存在慢的提供者累積請求的問題,比如:第二臺機器很慢,但沒掛,當請求調到第二臺時就卡在那,久而久之,所有請求都卡在調到第二臺上。
LeastActive LoadBalance
最少活躍調用數,相同活躍數的隨機,活躍數指調用前后計數差。使慢的提供者收到更少請求,因為越慢的提供者的調用前后計數差會越大。
ConsistentHash LoadBalance
一致性 Hash,相同參數的請求總是發(fā)到同一提供者。當某一臺提供者掛時,原本發(fā)往該提供者的請求,基于虛擬節(jié)點,平攤到其它提供者,不會引起劇烈變動。
Kafka 的分區(qū)分配策略
Kafka 中提供了多重分區(qū)分配算法(PartitionAssignor)的實現:
RangeAssignor
圖片
RangeAssignor 策略的原理是按照消費者總數和分區(qū)總數進行整除運算來獲得一個跨度,然后將分區(qū)按照跨度進行平均分配,以保證分區(qū)盡可能均勻地分配給所有的消費者。
對于每一個 Topic,RangeAssignor 策略會將消費組內所有訂閱這個 Topic 的消費者按照名稱的字典序排序,然后為每個消費者劃分固定的分區(qū)范圍,如果不夠平均分配,那么字典序靠前的消費者會被多分配一個分區(qū)。
RoundRobinAssignor
RoundRobinAssignor 的分配策略是將消費組內訂閱的所有 Topic 的分區(qū)及所有消費者進行排序后盡量均衡的分配(RangeAssignor 是針對單個 Topic 的分區(qū)進行排序分配的)。
StickyAssignor
從字面意義上看,Sticky 是“粘性的”,可以理解為分配結果是帶“粘性的”——每一次分配變更相對上一次分配做最少的變動(上一次的結果是有粘性的),其主要是為了實現以下兩個目標:
- 分區(qū)的分配盡量的均衡
- 每一次重分配的結果盡量與上一次分配結果保持一致
65 哥:哇,看來優(yōu)秀的系統都是相通的。
副本
副本是解決分布式集群高可用問題的。
在集群系統中,每個服務器節(jié)點都是不可靠的,每個系統都有宕機的風險,如何在系統中少量節(jié)點失效的情況下保證整個系統的可用性是分布式系統的挑戰(zhàn)之一。
副本就是解決這類問題的方案。副本同樣也可以提高并發(fā)處理能力,比如數據在不同的節(jié)點上可以讀寫分離,可以并行讀等。
在這里其實也有很多說法,如 Master-Salve、Leader-Follower、Primary-Shard、Leader-Replica 等等。
圖片
MySQL 的主從架構
目前,大部分的主流關系型數據庫都提供了主從熱備功能,通過配置兩臺(或多臺)數據庫的主從關系,可以將一臺數據庫服務器的數據更新同步到另一臺服務器上。
這既可以實現數據庫的讀寫分離,從而改善數據庫的負載壓力,也可以提高數據高可用,多份數據備份降低了數據丟失的風險。
圖片
Elasticsearch 的副本機制
在 ES 中有主分片和副本分片的概念。副本分片的主要目的就是為了故障轉移,如果持有主分片的節(jié)點掛掉了,一個副本分片就會晉升為主分片的角色從而對外提供查詢服務。
圖片
CAP 理論
在理論計算機科學中,CAP 定理(CAP theorem),又被稱作布魯爾定理(Brewer's theorem),它指出對于一個分布式計算系統來說,不可能同時滿足分布式系統一致性、可用性和分區(qū)容錯(即 CAP 中的"C","A"和"P"):
圖片
65 哥:什么是一致性、可用性和分區(qū)容錯性能?
- 一致性 (Consistency)
一致性意味著所有客戶端同時看到相同的數據,無論它們連接到哪個節(jié)點。要發(fā)生這種情況,每當將數據寫入一個節(jié)點時,必須立即將數據轉發(fā)或復制到系統中的所有其他節(jié)點,然后才能將寫入視為"成功"。
- 可用性 (Availability)
任何客戶端的請求都能得到響應數據,不會出現響應錯誤。換句話說,可用性是站在分布式系統的角度,對訪問本系統的客戶的另一種承諾:我一定會給您返回數據,不會給你返回錯誤,但不保證數據最新,強調的是不出錯。
- 分區(qū)容錯
分區(qū)即分布式系統中的通信中斷,兩個節(jié)點之間的丟失或暫時延遲的連接。分區(qū)容錯意味著群集必須繼續(xù)工作,盡管系統中的節(jié)點之間存在的通信故障。
這三種性質進行倆倆組合,可以得到下面三種情況:
- CA:完全嚴格的仲裁協議,例如 2PC(兩階段提交協議,第一階段投票,第二階段事物提交)
- CP:不完全(多數)仲裁協議,例如 Paxos、Raft
- AP:使用沖突解決的協議,例如 Dynamo、Gossip
CA 和 CP 系統設計遵循的都是強一致性理論。不同的是 CA 系統不能容忍節(jié)點發(fā)生故障。CP 系統能夠容忍 2f+1 個節(jié)點中有 f 個節(jié)點發(fā)生失敗。
Base 理論
圖片
CAP 理論表明,對于一個分布式系統而言,它是無法同時滿足 Consistency(強一致性)、Availability(可用性) 和 Partition tolerance(分區(qū)容忍性) 這三個條件的,最多只能滿足其中兩個。
在分布式環(huán)境中,我們會發(fā)現必須選擇 P(分區(qū)容忍)要素,因為網絡本身無法做到 100% 可靠,有可能出故障,所以分區(qū)是一個必然的現象。也就是說分區(qū)容錯性是分布式系統的一個最基本要求。
CAP 定理限制了我們三者無法同時滿足,但我們可以盡量讓 C、A、P 都滿足,這就是 BASE 定理。
BASE 理論是 Basically Available(基本可用),Soft State(軟狀態(tài))和 Eventually Consistent(最終一致性)三個短語的縮寫。
即使無法做到強一致性(Strong consistency),但每個應用都可以根據自身的業(yè)務特點,采用適當的方式來使系統達到最終一致性(Eventual consistency)。
基本可用 (Basically Available)
基本可用是指分布式系統在出現故障的時候,允許損失部分可用性,即保證核心可用。
電商大促時,為了應對訪問量激增,部分用戶可能會被引導到降級頁面,服務層也可能只提供降級服務,這就是損失部分可用性的體現。
軟狀態(tài) ( Soft State)
什么是軟狀態(tài)呢?相對于原子性而言,要求多個節(jié)點的數據副本都是一致的,這是一種“硬狀態(tài)”。
軟狀態(tài)指的是:允許系統中的數據存在中間狀態(tài),并認為該狀態(tài)不影響系統的整體可用性,即允許系統在多個不同節(jié)點的數據副本存在數據延時。
最終一致性 ( Eventual Consistency)
最終一致性是指系統中的所有數據副本經過一定時間后,最終能夠達到一致的狀態(tài)。
弱一致性和強一致性相反,最終一致性是弱一致性的一種特殊情況。
BASE 理論面向的是大型高可用、可擴展的分布式系統。與傳統 ACID 特性相反,不同于 ACID 的強一致性模型,BASE 提出通過犧牲強一致性來獲得可用性,并允許數據段時間內的不一致,但是最終達到一致狀態(tài)。
分布式是系統擴展的必然方向,分布式系統所遇到的問題是普遍,隨著大量優(yōu)秀的項目在分布式的道路上披荊斬棘,前人已經總結了大量豐富的理論。
并且不同領域的分布式系統也層出不窮,我們既應該學習好這些好的理論知識,也應該去多看看不同分布式系統的實現,總結它們的共性,發(fā)現它們在不同領域獨特的亮點權衡,更重要的,我們應該將所學用于日常項目的實踐當中,也應該在實踐中總結出更多的規(guī)律理論。