1. 程式人生 > >分散式系統詳解

分散式系統詳解

先講個黑色笑話:

半年前,一個誰也沒見過的日本浪人推出的理財產品突然在七俠鎮火爆起來,據說買上點屯著,不出幾月就能把同福客棧,甚至龍門鏢局都盤下。我們家小六的七舅老爺,賣掉祖宅也嚷嚷著要 all in。我覺得這事吧很是蹊蹺,好歹也是自家人嘛,不能讓老人家上當受騙 —— 所以 … 放著我來。我用我無雙的智慧,和堪比丞相的三寸不爛之舌給七舅老爺攔下來,讓他打消了念頭。沒出半年,小六七舅老爺全家就和我們斬了聯絡,死生不復相見。 – 摘自《無雙日記》

有朋友問,那做技術的,怎麼入行?

我雖不算入行,但知道技術是一脈相承的 —— blockchain 不是像孫猴子憑空從石頭裡蹦出來的,它有它的根。如果把一門門技術看做一棵棵拔地而起,隨時間漸漸豐腴而枝繁葉茂的榕樹,那麼 blockchain 是若干棵已然成年的榕樹交織而成的新枝(branch) —— 它快速成長,活力無限,樹冠已然蓋住了其它 branch 的鋒芒。如果你直接爬到它的樹冠上,想要抓其脈絡,在紛繁複雜麵前,會迷失方向;但若你從根部細細往上捋,線索就如錢老筆下的「真理」,赤裸裸而一覽無遺。

今天我們先尋其最重要的一個根:分散式系統。這個題目對網際網路從事者來說,看著可笑,誰敢說自己不瞭解分散式系統啊?然而,如果你只是躲在 load balancer 後面做些 stateless 的 service,而沒有真正去面對分散式系統那種讓人愉悅並憂傷著的不確定性,那麼,你可能並不真正瞭解分散式系統,因而本文還是值得一讀。

依舊例,我們還是先看 wikipedia,把概念先熟絡起來:

A distributed system is a model in which components located on networked computers communicate and coordinate their actions by passing messages.[1] The components interact with each other in order to achieve a common goal. Three significant characteristics of distributed systems are: concurrency of components, lack of a global clock, and independent failure of components. - Wikipedia

其中的的關鍵詞我已經勾勒出來:communication / coordinate,message passing,concurrency,lack of global clock,independent failure。我們看看這些東西,是如何引發不確定性的。

global clock

我們先說說全域性時鐘 —— global clock。它是分散式系統諸多難題的根源之一。

在單機系統裡,無論是 SMP 還是 NUMA,有且只有唯一的全域性時鐘,這個很容易辦到。

時間是什麼?拋開相對論,在狹義的區域性時空中,時間是因果的表象 —— 一個 cause 引發了一個 effect,這種因果產生了時間的概念:用時間(過去,現在,未來)可以更好地描繪因果。我們在 t0 執行一條指令,t1 得到結果,這結果不可能出現在指令執行之前,這便是時間帶給我們的確定性。所以,一個系統有一致的,大家都認可和遵循的時間,非常重要。

在分散式系統裡,每個系統都有自己的時鐘,即便用 NTP(Network Time Protocol)同步,大家也無法嚴格步調一致;就算時鐘的差異小到可以忽略不計,但取決於頻寬,當時的擁塞程度,CPU 的繁忙程度,多個系統互相之間傳送訊息的延遲還是非常地不確定。就跟一個團隊去會議室開會一樣,如果都根據自己的手錶來決定進入會議室的時間,那麼肯定會不一致;即便手錶時間一致,大家的走路的速度不同,最終進入會議室的時間,也是不一致。這種不一致會帶來很多問題,比如說 out of sync —— 大家都散會了,Alice 才抵達會場,所以她缺失了很多狀態的更新,於是她不知道手上的下一件事該做還是不該做。所以在分散式系統裡很多時候我們需要一致性,來確保某些東西是有序的,大家在同一個 page,否則這個系統會走入歧途。

要解決因為時鐘不同,步調不一致而導致的 out of sync 的問題,我們需要設法形成一個邏輯上的「時鐘」,讓大家都認可這個「時鐘」而不是自己的時鐘。這個邏輯時鐘的第一個實現是 Lamport timestamps(請記住 Lamport 這點陣圖靈獎獲得者,分散式系統的先驅,下文他還會上鏡)。Lamport timestamps 價值大於實際價值,並沒有系統實際使用,然而在它之上演進出的 vector clock 廣泛被 AWS S3,DynamoDB,Riak 等系統採用,用於確保同一個 object 的因果關係。我們看看 vector clock 的實現:
這裡寫圖片描述
這個演算法的思想很簡單:所有 node 都有一個包含所有 timestamp 的 vector,這是個邏輯「時鐘」。每個獨立的 node 自行處置屬於自己的 timestamp,使其有序;但當需要 coordinate 的時候(A 發訊息給 B),node A 要傳送自己對「時鐘」的掌握情況,node B 收到後,更新 vector 裡所有比自己已知更大的 timestamp。演算法如下(請自行 wiki 以獲得更準確的資訊):

每個 node 都有一個 timestamp vector,初始化為全 0。

如果某個 node k發生了某個事件,將其對應的 vector[k] + 1。

如果 node k 給 node j 發訊息,那麼先將 node k 自己的 vector[k] + 1,然後將整個 vector 連同 message 一起發給 node j,node j 將自己原有的 vector[j] + 1,再把 node k 發來的 vector 和自己合併(找最大值)。
歡迎訪問我們的技術交流群491389334
通過 vector clock,雖然沒有絕對的 global clock,但是我們在分散式系統裡能夠保證因果,從而消滅了在這個維度上的不確定性(還有其他不確定性!)。

我們可以看到,vector clock 的演算法嚴重依賴於節點間的信任,所以它只適用於一個可信賴的分散式環境。而作為執行在節點間互相併不信任的 P2P 網路上的 bitcoin,無法確保這一點。那麼,類似 bitcoin 這樣的分散式系統,是怎麼決定時間(因果)的呢?中本聰在 bitcoin 的設計中,巧妙地應用了 PoW 的產物,block 來作為系統的邏輯時間:

The solution we propose begins with a timestamp server. A timestamp server works by taking a hash of a block of items to be timestamped and widely publishing the hash, such as in a newspaper or Usenet post [2-5]. The timestamp proves that the data must have existed at the time, obviously, in order to get into the hash. Each timestamp includes the previous timestamp in its hash, forming a chain, with each additional timestamp reinforcing the ones before it.

所以,blockchain 不但承載了 ledger 的功能,chain 上的一個個 block 還是一個個 timestamp,代表著這個系統的過去,現在,以及未來,從而協調整個分散式系統步調一致地前進(且讓我再奶一下聰哥)。

看到這裡,我相信很多人有個疑問 —— 程式君,為什麼我做的分散式系統既不用關心 vector clock,也不用 PoW,整個系統也木有 global clock,怎麼還一樣執行得好好的?沒錯。你沒有感知,並不代表它不存在或者不重要 —— 你的系統裡的 postgres,consul,kafka,或者說,分散式系統裡一切看似中心化的部分,都使用了類似的機制,只不過它們幫你把這些細節遮蔽掉而已。

coordination / communication

好,瞭解了 lack of global clock 帶來的不確定性,以及如何應對這種不確定性,我們再看分散式系統裡下一個會引發不確定性的基礎組成部分:溝通協作。

單機系統,協作和溝通也是件輕而易舉的事情 —— 同一個執行緒,在 stack / register 上同步(取決於 ABI);不同執行緒,semaphore;不同 CPU,spin lock,memory barrier,反正大家生活在一個屋檐(時鐘)下,咋都有辦法。

分散式系統下就尷尬了。隔壁老王之所以被稱作隔壁老王,是因為你們兩家之間至少有一堵牆(住大 house 的有兩堵牆),所以在無法四目相對的情況下,你們溝通基本靠吼。吼是個文言文,在現代計算機文明中,我們管它叫:發訊息(message passing)。

發訊息前先要確保有合適的通道,你得先確保這個通道建立成功並且可以信賴:
這裡寫圖片描述
所謂可信賴,就是訊息在網路上不會丟失,我只要發了,對方的 application 就一定能收到(避免下圖所示的不確定性):
這裡寫圖片描述
而且既不會像下面這樣亂序,也不會把 partial message 交付給 application(保證訊息的完整性):
這裡寫圖片描述
有了可信賴的通道,我們可以通過 message 來達成共識。我們先看兩個人達成共識會遇到什麼障礙:
這裡寫圖片描述傳送的節點或者接收的節點可能會 crash —— 即便網路層保證了 application 一定會收到訊息,但我們無法避免 application 在處理訊息的時候掛掉。因而,message delivery 有兩種策略:at least once 或者 at most once。at least once 是指同一個訊息會被傳輸 1 到 n 次,而 at most once 是指同一個訊息會被傳輸 0 到 1 次。這很好理解,如果 messaging system 內建了重傳機制,並且將訊息持久化到磁碟中以保證即便程序崩潰訊息依舊能夠送達,那麼這就是 at least once。反之,如果沒有構建任何上述的機制,訊息送出後就並不理會,這是 at most once。在一個網路環境中,訊息的送達只能是上述兩種情況,不可能 exactly once,如果有人這麼說,那麼一定是在誤導。

at least once / at most once 並沒有解決不確定性的問題,所以我們還得再努努力 —— kafka / AWS kenisis / AWS SQS 實現了 essentially once 的 message delivery 機制。essentially once 是 at least once 的變種,它需要訊息層和應用層同心協力 —— 應用層處理完訊息,主動告知訊息層,令其刪除對應的訊息。如果你用過 SQS,應該能感受到這一點:SQS 保證當一個 application 在處理某個訊息時,訊息對分散式系統裡的其他人是不可見的,如果 application crash,訊息會在 visibility timeout 後重新可見,如果 application 處理完成,需要顯式地刪除這條訊息。
最終,我們可以通過訊息傳遞的機制,來達成一致:

這裡寫圖片描述
到目前為止,我們所談論的還主要是僅有你和老王參與的 message delivery 的問題。在一個分散式系統裡,任意兩兩節點間都可能有訊息往來,而由於缺乏全域性的時鐘,我們無法保證訊息是全域性有序的(TCP 只能保證相同發起人傳送的訊息時有序的),通過 vector clock 或者類似的機制,我們可以進一步保證訊息在因果關係上是有序的。這在大多數情況下,已經足夠好。

然而,在眾多參與者的情況下,即便我們保證了訊息區域性以及在因果關係上有序,我們還是無法保證所有參與者達成共識。如果大家就該不該做一件事情(比如來了一條資料,怎麼寫,寫到哪,誰來寫等)無法達成共識,那麼,這樣的系統依舊是不確定的。

於是有了 2PC(2 phase commit),3PC,Paxos,Raft 等在可信環境下的共識機制;同樣的,對於 blockchain 所面臨的不可信環境下(Byzantine General probelm),誕生了 BFT / PBFT,以及 PoW,PoS,DPoS,PoI,PoD,PoDDOS 等一堆 P 字輩靠譜或者不靠譜的共識演算法(很快,P 都不夠用了)。限於篇幅,關於共識演算法,我將另行撰文討論。

如果你更多瞭解訊息系統及訊息傳遞的 pattern,可以看看我之前的文章:ZeroMQ及其模式(再沉痛悼念一下 Pieter Hintjens)。

分散式系統中的坑

上文中我們已經把分散式系統中最基本的要素過了一下。接下來我們踩踩坑。

坑一:network is reliable。我們在訊息傳遞中,費盡心思做了很多事情,就是在跟並不 reliable 的 network 鬥爭。其實 packet loss / out of order 是屬於還好解決的問題;不那麼好解決的問題是:split brain(腦裂)。split brain 是基於 asymmetric consensus 的分散式系統(亦即常見的 master-slave cluster)的夢魘,一旦 master 過忙,導致 slave 認定它應該卸任,slave 接管後,因為冷啟動,也變得太忙,於是又切換回去 —— 於是 master / slave 都在一定時間內異常繁忙,replication 失敗,資料出現不一致,接下來雙方都認為對方 down 掉,自己成為 master。這就是 split brain。這種級別的問題,修復起來都麻煩(尤其是對於使用了 autoincrement,然後又被 foreign key 引用的資料)。

坑二:訊息傳遞的 latency 可以忽略不計。我們知道網路的 latency 是大致等於兩點間距離除以光速。北京到舊金山,極其理想的情況下,一個 round trip 也要 63ms(9516 x 2 / 300, 000),這是一個不小的時間了,如果使用停等的方式互相確認詳細,1s 僅僅能打十幾個來回。下圖是一個完整的計算機系統裡各個部分的 latency 的量級的介紹,大家都應該讀讀,心裡有個譜:
這裡寫圖片描述
坑三:網路頻寬不是問題。在 cloud 裡,頻寬不是問題。但由眾多家庭使用者參與的 P2P network,頻寬,尤其是上行頻寬是明顯受限的。我家的百兆網路,上行經常也就是 12Mbps。我在上一篇文章 比特幣淺析 裡談到為何比特幣使用 1M block,是因為如果這個網路的初衷是所有人都可以參與進來,那麼,要考慮到普通使用者的頻寬承受能力。假設你的電腦費勁巴拉算出一個區塊,你要立刻將其廣播到 7 個鄰居,如果 30s 內完成廣播,那麼你需要 1.86 Mbps(1MB x 8bit x 7 peers / 30 sec)的頻寬;如果 block 是 8M,那麼你需要 15 Mbps 的上行頻寬。所以,如果設計執行在 cloud 裡(> 1Gbps)的分散式系統,可以不必太過在意頻寬,但是要做一個人人都用的起的 blockchain,起碼不要拍腦門設定 block 大小。

坑四:參與的節點是同構的。像 bitcoin 這樣的分散式系統,參與的節點小到手機(錢包軟體),大到專門開發的帶有 ASIC 或者 GPU 陣列的礦機,不一而足。它們所使用的網路,從 wired,wireless 一路到 cellular,satellite。所以我們要考慮到區別如此之大,範圍如此之廣的參與者。礦機自然要獲取全部資料,但你讓手機使用者還下載 160G 的 chain,那就是強人所難。bitcoin 對此專門考慮到 blocker headers + merckle tree path + SPV 的方案,允許小型裝置只需下載少量資料(幾百兆),就可以充當錢包,驗證和自己相關的交易。這是我們需要了解的。

CAP 理論

分散式系統中,繞不過去的一個話題是 CAP 理論:即對於 Consistency,Availability 和 Partition tolerance,你只能保證其中兩個,而犧牲第三個。
這裡寫圖片描述
我來簡單解釋一下:

Consistency:所有客戶端看到的同樣的資料。這裡所說的 consistency,是指 atomic consistency (linearizability)。

Availability:每個客戶端任何時候都可以讀寫。

Partition tolerance:當網路出現 partition(split brain)時,系統仍舊可以工作。換句話說,為了能夠支援 Partition tolerance,系統需要能夠容忍任意多的訊息的丟失。

談論一個系統是 CA,CP,還是 AP,其實是把複雜的問題過分簡化。但分類有分類的好處:它便於比較和記憶。

MongoDB 在上圖中被我歸到了 CP,這是因為寫入是預設 safe=true,也就是犧牲了 Availability,只能寫入 master。然而,這種情況下,MongoDB 仍然難說是 Consistency 的,只有當 readConcern 設為 linearizability,才算得上 Consistency。所以,MongoDB 的 CP 屬性很勉強,不同的設定下很難將其歸屬到某一個分類中。

現在的分散式系統,其實對 CAP 三者都有考慮,只不過是優先順序的問題 —— 我更看重哪兩個,而願意犧牲第三個?MySQL 在 master/slave 的配置下,犧牲了 partition tolerance,但我們也可以將其配置成 cluster,犧牲 Availability,才成全 Partition Tolerance。

考大家一個小問題:bitcoin 是 CA,還是 CP,還是 AP?

先寫到這裡。有機會再寫本文沒有展開講的共識機制,它是分散式系統的基石

相關推薦

分散式系統--基礎知識(通訊)

                       分散式系統詳解--基礎知識(通訊)          上一篇文章我們寫到了&nb

分散式系統--基礎知識(執行緒)

                     分散式系統詳解--基礎知識(執行緒) 一、導讀           前面跟大家講了一下&n

分散式系統--基礎知識(概論)

                       分散式系統詳解--基礎知識(概論) 一、 引言         由於網上介紹的分散式的

分散式系統--架構簡介(微服務)

                       分散式系統詳解--架構簡介(微服務)           前面的一個集合我們

分散式系統--基礎知識(併發)

                      分散式系統詳解--基礎知識(併發)        在前面曾經寫了一篇文章 時  分散式系統詳

分散式系統--基礎知識(安全)

                          分散式系統詳解--基礎知識(安全)          前一篇文章

分散式系統--基礎知識(CAP)

                           分散式系統詳解--基礎知識(CAP)        前面呢,文

分散式系統--框架(Hadoop-叢集搭建)

                 分散式系統詳解--框架(Hadoop-叢集搭建)        前面的文章也簡單介紹了,hadoop的環境搭建分為三種,單機版,偽分

分散式系統--架構(Hadoop-克隆伺服器)

                   分散式系統詳解--架構(Hadoop-克隆伺服器)          分散式系統上一個呢,寫了一下分散式系統

分散式系統--框架(Hadoop-單機版搭建)

                    分散式系統詳解--框架(Hadoop-單機版搭建)        前面講了這麼多的理論知識,也有一些基礎的小知識點,

分散式系統--Linux(許可權)

                          分散式系統詳解--Linux(許可權)         之前再window

分散式系統--框架(Hadoop--RPC協議)

                   分散式系統詳解--框架(Hadoop--RPC協議)        在之前的一篇文章當中已經寫過一篇文章是關於RPC通

分散式系統--框架(Hadoop--JAVA操作HDFS檔案)

       分散式系統詳解--框架(Hadoop--JAVA操作HDFS檔案)         前面的文章介紹了怎麼將整個集群系統搭建起來,並進行了有效的測試。為了解決登入一臺伺服器登入其他伺服器需要多次輸入密碼的

分散式系統--框架(Hadoop-基本shell命令)

               分散式系統詳解--框架(Hadoop-基本shell命令)         前面的文章我們已經將一個叢集搭建好了,現在就需要知道一些關於hadoo

分散式系統--框架(Hadoop-Ssh免密登陸配置)

              分散式系統詳解--框架(Hadoop-Ssh免密登陸配置)          配置Ssh非常簡單,其實就是為了避免將來叢集機器變得很多導致操作本機、

分散式系統--框架(Zookeeper-簡介和叢集搭建)

             分散式系統詳解--框架(Zookeeper-簡介和叢集搭建) 一、Zookeeper簡介 1.1 Zookeeper是什麼?        

分散式系統--框架(Hadoop-HDFS的HA搭建及測試)

              分散式系統詳解 - 框架(Hadoop的HDFS的HA搭建及測試) 一,背景概述 解決問題1:通過上面的圖我們可以明確的看出來,如果的Namenode壞掉了,那我們的整個叢集可以說就是要癱瘓了也就是單節點故障問題。 於是現在就出現了另外一

分散式系統

先講個黑色笑話: 半年前,一個誰也沒見過的日本浪人推出的理財產品突然在七俠鎮火爆起來,據說買上點屯著,不出幾月就能把同福客棧,甚至龍門鏢局都盤下。我們家小六的七舅老爺,賣掉祖宅也嚷嚷著要 all in。我覺得這事吧很是蹊蹺,好歹也是自家人嘛,不能讓老人家上當受

[轉]Ceph:OpenStack標配&Linux PB級分散式檔案系統

Ceph:一個 Linux PB 級分散式檔案系統作為一名儲存行業的架構師,我對檔案系統情有獨鍾。這些系統用來儲存系統的使用者介面,雖然它們傾向於提供一系列類似的功能,但它們還能夠提供差異顯著的功能。Ceph 也不例外,它還提供一些您能在檔案系統中找到的最有趣的功能。Ceph

FastDFS 分散式檔案系統

## 什麼是檔案系統 ![](https://mrhelloworld.com/resources/articles/fastdfs/20180810094347_11565.jpg " ")   檔案系統是作業系統用於在磁碟或分割槽上組織檔案的方法和資料結構。磁碟空間是什麼樣的我們並不清楚,但檔案系統可