本書(shū)深入剖析了主流開(kāi)源分布式系統(tǒng)模式,包括模式中的常見(jiàn)問(wèn)題和解決方案,并展示了Kafka和Kubernetes等系統(tǒng)的真實(shí)代碼示例,以幫助企業(yè)架構(gòu)師和開(kāi)發(fā)人員更好地理解這些系統(tǒng)的工作原理,以及分布式系統(tǒng)的設(shè)計(jì)原則,為應(yīng)對(duì)數(shù)據(jù)存儲(chǔ)在多臺(tái)服務(wù)器上時(shí)可能出現(xiàn)的各種問(wèn)題做好準(zhǔn)備。通過(guò)閱讀本書(shū),讀者將:了解什么是分布式系統(tǒng),以及為什么需要分布式系統(tǒng)。更深入地理解分布式系統(tǒng)模式設(shè)計(jì)所面臨的挑戰(zhàn),以選擇合適的云服務(wù)和產(chǎn)品。理解包括數(shù)據(jù)庫(kù)、內(nèi)存數(shù)據(jù)網(wǎng)格、消息代理,以及各種云服務(wù)在內(nèi)的系統(tǒng)的實(shí)現(xiàn)原理。自信地瀏覽開(kāi)源代碼庫(kù),并清晰地看到模式和解決方案如何映射到如Kafka和Kubernetes這樣的真實(shí)世界系統(tǒng)中。本書(shū)對(duì)于分布式架構(gòu)工程師以及想要構(gòu)建自己的分布式系統(tǒng)的開(kāi)發(fā)者來(lái)說(shuō),是一本有價(jià)值的參考書(shū)。
介紹了在集群節(jié)點(diǎn)之間建立有效網(wǎng)絡(luò)通信的技巧。介紹了常用的分區(qū)方案,并深入研究?jī)呻A段提交協(xié)議的復(fù)雜性。介紹了數(shù)據(jù)庫(kù)中邏輯時(shí)間戳的使用,以幫助讀者理解數(shù)據(jù)版本控制的基本概念。闡述了Paxos和Raft等共識(shí)算法的構(gòu)建塊,以確保分布式系統(tǒng)中副本的一致性。闡述了用于實(shí)現(xiàn)集群協(xié)調(diào)任務(wù)(如組成員資格、故障檢測(cè)以及健壯的集群協(xié)調(diào))的機(jī)制。涵蓋了數(shù)據(jù)復(fù)制模式、數(shù)據(jù)分區(qū)模式、分布式時(shí)間模式、集群管理模式以及節(jié)點(diǎn)間通信模式等內(nèi)容。
本書(shū)寫作緣由
在2017年,我參與了一個(gè)名為“Thirty Meter Telescope”(TMT)的大型光學(xué)望遠(yuǎn)鏡軟件系統(tǒng)的開(kāi)發(fā)項(xiàng)目。我們的任務(wù)是構(gòu)建供各個(gè)子系統(tǒng)使用的核心框架和服務(wù)。這些子系統(tǒng)組件必須能夠相互發(fā)現(xiàn)與檢測(cè)組件故障,并能夠存儲(chǔ)有關(guān)各組件的元數(shù)據(jù)。負(fù)責(zé)這些信息存儲(chǔ)的服務(wù)必須是容錯(cuò)的。考慮到望遠(yuǎn)鏡生態(tài)系統(tǒng)的特殊性,我們不能采用現(xiàn)成的產(chǎn)品和框架,只能從零開(kāi)始,打造適用于不同軟件子系統(tǒng)的核心框架和服務(wù),從本質(zhì)上說(shuō),我們要建立的是一個(gè)分布式系統(tǒng)。
我曾設(shè)計(jì)并構(gòu)建過(guò)依賴于Kafka、Cassandra和MongoDB等產(chǎn)品的企業(yè)系統(tǒng),它們使用AWS或GCP等云服務(wù)。這些產(chǎn)品和服務(wù)都是分布式的,解決了一系列相似的問(wèn)題。對(duì)于TMT系統(tǒng),我們必須自行開(kāi)發(fā)解決方案。為了驗(yàn)證和比較這些成熟的產(chǎn)品,我們需要更深入地理解它們的內(nèi)部機(jī)制。了解這些云服務(wù)和產(chǎn)品的構(gòu)建方式及其背后的原因是必要的,它們的官方文檔往往太過(guò)產(chǎn)品化,不利于達(dá)成我們的目標(biāo)。
關(guān)于構(gòu)建分布式系統(tǒng)的信息分散在各種研究論文中。然而,這些學(xué)術(shù)資源也有局限,它們往往只關(guān)注特定領(lǐng)域而忽略了相關(guān)主題。以“Consensus: Bridging Theory and Practice”(Ongaro,2014)這篇精彩的論文為例,它詳細(xì)解釋了實(shí)現(xiàn)Raft共識(shí)算法的過(guò)程。但你不會(huì)從中了解到像etcd這樣的產(chǎn)品如何使用Raft來(lái)追蹤集群成員資格和其他產(chǎn)品的相關(guān)元數(shù)據(jù),如Kubernetes。Leslie Lamport的著名論文“Time, Clocks, and the Ordering of Events in a Distributed System”(Lamport,1978)中討論了邏輯時(shí)鐘的使用,但它并未解釋像MongoDB這樣的產(chǎn)品如何使用邏輯時(shí)鐘作為版本號(hào)來(lái)控制數(shù)據(jù)的版本。
我相信編寫代碼是驗(yàn)證理解正確與否的最佳方式。正如Martin Fowler所說(shuō):“代碼就像數(shù)學(xué),我們必須消除其中的歧義!币虼耍瑸榱松羁汤斫夥植际较到y(tǒng)的基礎(chǔ)模塊,我決定自己動(dòng)手構(gòu)建這些產(chǎn)品的簡(jiǎn)化版本。我從打造一個(gè)玩具版的Kafka開(kāi)始,一旦有了合理的版本,我就用它來(lái)探討分布式系統(tǒng)的一些基本概念。這種方法被證明非常有效。為了驗(yàn)證通過(guò)代碼來(lái)闡釋概念的效果,我在Thoughtworks開(kāi)展了一系列內(nèi)部研討會(huì),這些研討會(huì)對(duì)我?guī)椭鷺O大。因此,我將這種方法擴(kuò)展到了Cassandra、Kubernetes、Akka、Hazelcast、MongoDB、YugabyteDB、CockroachDB、TiKV和Docker Swarm等產(chǎn)品。我提取了代碼片段來(lái)理解這些產(chǎn)品的構(gòu)建模塊。果不其然,這些模塊之間存在許多相似之處。幾年前,我偶然與Martin Fowler討論過(guò)這個(gè)話題,他建議我將其整理成模式。本書(shū)便是我與Martin Fowler合作,將分布式系統(tǒng)中的共通的構(gòu)建模塊整理成模式的成果。
本書(shū)讀者
在當(dāng)今軟件架構(gòu)和開(kāi)發(fā)的選擇豐富多樣的背景下,面對(duì)眾多分布式產(chǎn)品和云服務(wù),架構(gòu)師和開(kāi)發(fā)者面臨著復(fù)雜的設(shè)計(jì)抉擇。這些產(chǎn)品和服務(wù)的設(shè)計(jì)折中可能難以直觀理解。單憑閱讀文檔是遠(yuǎn)遠(yuǎn)不夠的。比如,當(dāng)我們考慮“AWS MemoryDB通過(guò)復(fù)制的事務(wù)日志確保了數(shù)據(jù)的持久性”“Apache Kafka現(xiàn)能獨(dú)立于ZooKeeper運(yùn)作”或者“Google Spanner通過(guò)同步的全球時(shí)間來(lái)維護(hù)外部一致性”這樣的句子時(shí),該如何理解這些技術(shù)性描述?
為了深入了解,專業(yè)人士往往依賴于產(chǎn)品供應(yīng)商的認(rèn)證培訓(xùn)。然而,這些認(rèn)證大多局限于特定產(chǎn)品,關(guān)注的是表層特性,而非背后的技術(shù)原理。專業(yè)開(kāi)發(fā)者需要對(duì)這些技術(shù)細(xì)節(jié)有直觀的把握,既能具體到在源代碼層面描述,又能通用到適應(yīng)不同場(chǎng)景。這正是模式的價(jià)值所在。本書(shū)介紹的模式旨在幫助從業(yè)者深入理解各種產(chǎn)品和服務(wù)的內(nèi)在機(jī)制,以便做出明智且有效的決策。
本書(shū)的主要讀者將是這些專業(yè)人士。除了那些需要與現(xiàn)有的分布式系統(tǒng)打交道的人員,還有一部分讀者可能需要構(gòu)建自己的分布式系統(tǒng)。我期望本書(shū)中的模式能夠?yàn)檫@些讀者提供一些有價(jià)值的參考,并幫助他們領(lǐng)先一步。書(shū)中引用了許多不同產(chǎn)品的設(shè)計(jì)方案,這些信息對(duì)讀者來(lái)說(shuō)同樣有益。
關(guān)于代碼示例的說(shuō)明
本書(shū)中大多數(shù)模式都提供了代碼示例。這些代碼示例建立在我研究這些模式時(shí),對(duì)各種產(chǎn)品所做的微型實(shí)現(xiàn)基礎(chǔ)之上。選擇編程語(yǔ)言的依據(jù)是它的普及度和可讀性—Java便是一個(gè)優(yōu)秀的選擇。示例中只用到了Java最基本的語(yǔ)言特性,即方法和類,這在大多數(shù)編程語(yǔ)言中都是通用的。即便是只熟悉其他編程語(yǔ)言的讀者,也應(yīng)該能夠輕松地理解這些代碼示例。不過(guò),需要明確的是,本書(shū)并非專為某個(gè)具體的軟件平臺(tái)編寫。一旦掌握了這些代碼示例,你會(huì)發(fā)現(xiàn)無(wú)論是C++、Rust、Go、Scala還是Zig,其代碼庫(kù)中都有這些模式的影子。我期望的是,通過(guò)熟悉這些代碼示例和模式,你將能更加輕松地閱讀和理解各種開(kāi)源產(chǎn)品的源代碼。
閱讀指南
本書(shū)共分為六部分。首先是兩章敘述性章節(jié),這些章節(jié)構(gòu)成了第一部分,它們涵蓋了分布式系統(tǒng)設(shè)計(jì)的基本主題,介紹了分布式系統(tǒng)設(shè)計(jì)中的挑戰(zhàn)及其解決策略,但并未深入討論這些策略的細(xì)節(jié)。
第二部分至第六部分提供了按模式結(jié)構(gòu)化的詳盡解決方案。這些模式被劃分為四個(gè)核心類別:復(fù)制、分區(qū)、集群管理和網(wǎng)絡(luò)通信。每一類都是構(gòu)建分布式系統(tǒng)的關(guān)鍵要素。
請(qǐng)將這些模式當(dāng)作一種參考手冊(cè),不需要逐字逐句地閱讀。你可以先瀏覽敘述性章節(jié),以獲得對(duì)本書(shū)內(nèi)容的整體理解,再根據(jù)個(gè)人興趣和實(shí)際需求,深入研究各個(gè)模式。
我希望這些模式能夠協(xié)助同行軟件專業(yè)人員在工作中做出明智的決策。
Unmesh Joshi (烏梅什·喬希)軟件架構(gòu)領(lǐng)域的領(lǐng)軍人物,Thoughtworks 首席顧問(wèn),擁有超過(guò)24年的IT行業(yè)經(jīng)驗(yàn)。分布式系統(tǒng)領(lǐng)域的資深專家,對(duì)分布式系統(tǒng)的設(shè)計(jì)和實(shí)現(xiàn)有著深刻的理解,對(duì)分布式系統(tǒng)的架構(gòu)模式有系統(tǒng)的梳理和總結(jié)。在 Scala、Akka、Kafka、Cassandra、Kubernetes、Docker和云服務(wù)等技術(shù)領(lǐng)域積累了豐富的經(jīng)驗(yàn),這些技術(shù)專長(zhǎng)使他能夠從理論到實(shí)踐全面掌握分布式系統(tǒng)的核心問(wèn)題。
譯者序
推薦序
前言
致謝
第一部分 概述
第1章 分布式系統(tǒng) 2
1.1 單服務(wù)器的限制 3
1.2 業(yè)務(wù)邏輯和數(shù)據(jù)層分離 4
1.3 數(shù)據(jù)分區(qū) 4
1.4 故障觀察 5
1.5 復(fù)制:屏蔽故障 6
1.5.1 進(jìn)程終止甚至崩潰 6
1.5.2 網(wǎng)絡(luò)延遲 6
1.5.3 進(jìn)程暫!6
1.5.4 時(shí)鐘不同步 7
1.6 定義分布式系統(tǒng) 7
1.7 模式方法 7
第2章 模式概述 9
2.1 在單服務(wù)器上保持?jǐn)?shù)據(jù)的彈性 10
2.2 競(jìng)爭(zhēng)性更新 11
2.3 處理主節(jié)點(diǎn)失效 12
2.4 依托“世代時(shí)鐘”解決多節(jié)點(diǎn)故障問(wèn)題 14
2.5 符合仲裁機(jī)制方可提交日志記錄 17
2.6 從節(jié)點(diǎn)基于高水位標(biāo)記提交 19
2.7 主節(jié)點(diǎn)用消息隊(duì)列來(lái)保持對(duì)眾多客戶端的響應(yīng) 23
2.8 由從節(jié)點(diǎn)處理讀請(qǐng)求以減輕主節(jié)點(diǎn)的負(fù)擔(dān) 29
2.9 把大量數(shù)據(jù)分散到多節(jié)點(diǎn)分區(qū) 30
2.10 通過(guò)復(fù)制分區(qū)提高集群彈性 32
2.11 跨分區(qū)維持一致性至少需要兩個(gè)階段 33
2.12 分布式系統(tǒng)的順序不能依賴于系統(tǒng)時(shí)間戳 35
2.13 一致性核心負(fù)責(zé)管理數(shù)據(jù)集群的成員資格 42
2.14 使用Gossip傳播機(jī)制來(lái)管理分布式集群 45
第二部分 數(shù)據(jù)復(fù)制模式
第3章 預(yù)寫日志 53
3.1 問(wèn)題的提出 54
3.2 解決方案 54
3.2.1 實(shí)現(xiàn)考慮 56
3.2.2 在事務(wù)存儲(chǔ)中的使用 56
3.2.3 與事件溯源對(duì)比 57
3.3 示例 58
第4章 日志分段 59
4.1 問(wèn)題的提出 60
4.2 解決方案 60
4.3 示例 61
第5章 低水位標(biāo)記 62
5.1 問(wèn)題的提出 63
5.2 解決方案 63
5.2.1 基于快照的低水位標(biāo)記 63
5.2.2 基于時(shí)間的低水位標(biāo)記 64
5.3 示例 65
第6章 主節(jié)點(diǎn)與從節(jié)點(diǎn) 66
6.1 問(wèn)題的提出 67
6.2 解決方案 67
6.2.1 主節(jié)點(diǎn)選舉 67
6.2.2 僅有多數(shù)讀/寫不足以提供強(qiáng)一致性保證 72
6.3 示例 72
第7章 心跳機(jī)制 73
7.1 問(wèn)題的提出 74
7.2 解決方案 74
7.2.1 小型集群:基于共識(shí)算法的系統(tǒng) 75
7.2.2 技術(shù)考慮 76
7.2.3 大型集群:基于Gossip協(xié)議 76
7.3 示例 77
第8章 多數(shù)法定節(jié)點(diǎn)數(shù) 78
8.1 問(wèn)題的提出 79
8.2 解決方案 79
8.2.1 決定集群中服務(wù)器的數(shù)量 79
8.2.2 靈活的多數(shù)法定節(jié)點(diǎn)數(shù) 80
8.3 示例 81
第9章 世代時(shí)鐘 82
9.1 問(wèn)題的提出 83
9.2 解決方案 83
9.3 示例 86
第10章 高水位標(biāo)記 87
10.1 問(wèn)題的提出 88
10.2 解決方案 88
10.3 示例 92
第11章 Paxos 93
11.1 問(wèn)題的提出 94
11.2 解決方案 94
11.2.1 協(xié)議流程 94
11.2.2 鍵值存儲(chǔ)示例 101
11.2.3 彈性Paxos 105
11.3 示例 105
第12章 復(fù)制日志 106
12.1 問(wèn)題的提出 107
12.2 解決方案 107
12.2.1 Multi-Paxos和Raft 107
12.2.2 復(fù)制客戶端請(qǐng)求 108
12.2.3 主節(jié)點(diǎn)選舉 113
12.2.4 技術(shù)考慮 118
12.2.5 推送與拉取 120
12.2.6 日志中有什么 120
12.3 示例 125
第13章 單一更新隊(duì)列 126
13.1 問(wèn)題的提出 127
13.2 解決方案 127
13.2.1 隊(duì)列的選擇 130
13.2.2 使用通道和輕量級(jí)線程 131
13.2.3 限流 131
13.2.4 其他考慮 132
13.3 示例 132
第14章 請(qǐng)求等待列表 133
14.1 問(wèn)題的提出 134
14.2 解決方案 134
14.3 示例 139
第15章 冪等接收器 140
15.1 問(wèn)題的提出 141
15.2 解決方案 141
15.2.1 使已保存的客戶端請(qǐng)求過(guò)期 144
15.2.2 移除已注冊(cè)的客戶端 145
15.2.3 最多一次、至少一次和恰好一次操作 145
15.3 示例 146
第16章 由從節(jié)點(diǎn)處理讀請(qǐng)求 147
16.1 問(wèn)題的提出 148
16.2 解決方案 148
16.2.1 尋找最近的副本 148
16.2.2 連接斷開(kāi)或慢速?gòu)墓?jié)點(diǎn) 151
16.2.3 讀寫一致性 151
16.2.4 線性化讀 154
16.3 示例 154
第17章 版本化值 155
17.1 問(wèn)題的提出 156
17.2 解決方案 156
17.2.1 版本化鍵的排序 156
17.2.2 讀多個(gè)版本 159
17.2.3 MVCC和事務(wù)隔離性 160
17.2.4 使用類似RocksDB的存儲(chǔ)引擎 161
17.3 示例 162
第18章 版本向量 163
18.1 問(wèn)題的提出 164
18.2 解決方案 164
18.2.1 版本向量比較 165
18.2.2 在鍵值存儲(chǔ)中使用版本向量 167
18.3 示例 174
第三部分 數(shù)據(jù)分區(qū)模式
第19章 固定分區(qū) 177
19.1 問(wèn)題的提出 178
19.2 解決方案 178
19.2.1 選擇哈希函數(shù) 179
19.2.2 將分區(qū)映射到集群節(jié)點(diǎn) 179
19.2.3