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