《Designing Data-Intensive Applications》第8章 讀書筆記(1):分布式系統(tǒng)的問題

1.說明

前面講的分布是遇到的一些故障,實際還是太樂觀了,分布式系統(tǒng)的的真實場景,問題要嚴重得多。
這一章,從最消極的角度,講解分布式系統(tǒng)實際上會遇到的多個問題。

2.fault以及partial failures

在單機環(huán)境中,一個優(yōu)秀的軟件,要么完全正常工作,要么完全broken,不會出現(xiàn)中間某種狀態(tài)。
這種是確定性的(deterministic)

而在分布式環(huán)境下,情況就不一樣了。
各個機器由網(wǎng)絡互相連接,會有各種非預期的原因,導致系統(tǒng)處于部分癱瘓,部分正常工作的情況。稱為partial failures
這些是非確定性的(non deterministic)。因此,理想化的系統(tǒng)模型不再可行。

因此,設計一個分布式的系統(tǒng)時,必須允許"partial failures"的出現(xiàn),并且構(gòu)建一個容錯的機制,對于不同的fault,需要明確需要產(chǎn)生的對應行為。

3.不可靠的網(wǎng)絡

這里討論的分布式系統(tǒng),是僅由網(wǎng)絡互連來完成通信的。每個機器有自己的內(nèi)存,磁盤。并且每個機器不能訪問其他機器的內(nèi)存和磁盤(稱為shared-nothing系統(tǒng))

shared-nothing 并不是唯一構(gòu)建分布式系統(tǒng)的方式,但是由于以下原因而流行:
1.廉價,不需要額外的硬件支持。
2.能利用云計算服務
3.能通過冗余來實現(xiàn)高可用性

大部分數(shù)據(jù)中心的網(wǎng)絡是異步網(wǎng)絡(asynchronous packet networks)
每個節(jié)點發(fā)給另外一個節(jié)點信息,但是網(wǎng)絡不保證信息何時到達以及信息是否到達

比如會出現(xiàn)如下幾種問題

1.請求丟失(比如網(wǎng)線被扒了)
2.請求在隊列中,排隊發(fā)送(比如zk和rocketmq都會這樣)
3.接收方機器掛了
4.接收方暫時沒有響應(比如進行gc中)
5.接收方已經(jīng)處理了請求,但是response在網(wǎng)絡中丟了
6.接收方的response也在排隊

比如下圖


image.png

當你發(fā)送請求卻還沒收到回復時,根本不知道是什么原因?qū)е碌?/p>

處理這個問題的通常方法是超時:一段時間后,發(fā)送方放棄等待,并假定響應不會到達。但是,當超時發(fā)生時,遠程節(jié)點可能已經(jīng)得到請求并進行了處理。

3.1 網(wǎng)絡故障實踐

有沒有方法能讓網(wǎng)絡變得可靠呢?似乎至今都沒有成功。
因此,一定要處理網(wǎng)絡故障,不單單表示忍受他們的發(fā)生,而是要合理的解決問題(如告訴用戶網(wǎng)絡中斷等)

3.2 故障檢測

有些系統(tǒng)需要自動檢測故障節(jié)點

1.負載均衡器停止發(fā)送請求給掛掉的節(jié)點
2.單leader模型中,leader掛了的話其他follower要精選出新的leader

不幸的是,有些場景甚至很難判斷節(jié)點是否掛了.
有些隱式的方式如管理頁面,通知腳本等可以讓用戶知道一個節(jié)點是否掛掉.

3.3 超時以及無限延遲

超時作為判斷的重要標準,到底應該設置多長呢?答案卻不簡單。

如果超時標準過長,則用戶等待的時間更長。
如果超時標準過短,則有可能誤判,造成消息重新發(fā)送一次給另外一個節(jié)點。

目前,大部分的一部網(wǎng)絡都不會有無限期的延遲(unbounded delays, that is, they try to deliver packets as
quickly as possible, but there is no upper limit on the time it may take for a packet to
arrive)

從超時檢測的角度來看,超時的時間也不能設置的太低也不能太高,

3.4 網(wǎng)絡阻塞,排隊

現(xiàn)實世界中,會有交通堵塞。網(wǎng)絡中的情況也一樣。

1.如果多個節(jié)點不斷地給一個節(jié)點法寶,那么網(wǎng)絡會讓這些包排隊,依次送達目標節(jié)點
2.當包到達目標節(jié)點時,目標節(jié)點可能由于CPU正忙而讓請求排隊,等候處理
3.TCP本身會有flow control等

上面的第一個情況見下圖


發(fā)送給port 3的請求排隊

諸多因素導致了網(wǎng)絡延遲,在這種環(huán)境下,只能實驗性的選擇超時參數(shù)

1.測量一段時間內(nèi)多個機器間的網(wǎng)絡往返時間的分布情況,決定出預期的延遲時間
2.結(jié)合自身應用的特性,在超時時間過長或者過短的trade off之間決定合適的時間
3.甚至可以避免配置常量超時時間,有些系統(tǒng)按上述方法自適應的調(diào)節(jié)timeout時間,如Akka的超時器,Cassandra的動態(tài)檢測,TCP的超時重傳。

3.5 同步網(wǎng)絡 VS 異步網(wǎng)絡

有沒有什么辦法能讓網(wǎng)絡在固定的最大延遲內(nèi)發(fā)送包呢
為了回答這個問題,先引入電話網(wǎng)絡的例子

1.當您通過電話網(wǎng)絡撥打電話時,它會建立一條電路:
沿著兩個呼叫者之間的整個路由,為呼叫分配固定的,有保證的帶寬量
該電路保持到通話結(jié)束
(比如ISDN網(wǎng)絡以每秒4000幀的固定速率運行)

2.呼叫建立后,每個幀內(nèi)(每個方向)分配16位空間。
因此,在通話期間,每一方都保證能夠每隔250微秒發(fā)送正好16位的音頻數(shù)據(jù)

上面這種網(wǎng)絡稱為synchronous,

即使數(shù)據(jù)會經(jīng)過多個路由,但是不會出現(xiàn)排隊情況,
因為呼叫的16位空間已經(jīng)在網(wǎng)絡的下一跳中保留。
因為沒有排隊,所以
網(wǎng)絡的最大端到端延遲是固定的。我們稱之為有限的延遲(bounded delay)

3.5.1 能否讓網(wǎng)絡延遲可預測

電話網(wǎng)絡的電路和TCP的連接是非常不一樣的.TCP會盡可能的里歐陽一切帶寬

你可以給TCP一個可變大小的塊數(shù)據(jù)(例如電子郵件或網(wǎng)頁),并且會盡可能在最短的時間內(nèi)傳輸
但是當TCP連接空閑時,也不使用任何帶寬。

以太網(wǎng)和IP是分組交換(packet-switched)協(xié)議,這些協(xié)議受到排隊的影響,從而導致網(wǎng)絡無限延遲。
為什么要用分組交換呢?是為了應對突發(fā)的交通情況

電路適合音頻或視頻通話,這需要在呼叫期間每秒傳送相當恒定的比特數(shù)。
而請求網(wǎng)頁,發(fā)送電子郵件或上傳文件,則沒有任何特定的帶寬需求 - 只是希望它盡快完成

所以,和電路相反,TCP根據(jù)可用的網(wǎng)絡容量,動態(tài)調(diào)整數(shù)據(jù)傳輸速率。

最新的技術并不允許我們對于延遲有任何的保證,必須假設網(wǎng)絡會有擁堵,排毒以及無限延遲這些情況的出現(xiàn).

4.不可靠的時間

在分布式系統(tǒng)中,時間是一件棘手的事情,因為通信不是瞬時的:消息經(jīng)過網(wǎng)絡從一臺機器轉(zhuǎn)到另一臺機器需要時間。
消息接收的時間總是比發(fā)送的時間晚,但由于網(wǎng)絡中的可變延遲,我們不知道以后會有多少延遲。很難確定多臺機器處理的邏輯與順序。

每臺機器都有自己的時鐘,通常是一個石英晶體振蕩器。這些設備并不完全準確,所以每臺機器都有自己的時間,它可能比其他機器稍快或慢一些。

存在同步時鐘的網(wǎng)絡協(xié)議:最常用的機制是網(wǎng)絡時間協(xié)議(NTP),它允許計算機時鐘根據(jù)一組服務器報告的時間進行調(diào)整。服務器可以從更精確的時間源獲取時間。

4.1 單調(diào)時間 VS Time-of-Day 時鐘

現(xiàn)在至少有兩種不同的時鐘:Time-of-Day時鐘 以及 單調(diào)時鐘,需要區(qū)分他們

4.1.1 Time-of-day clocks

就是類似java中的System.currentTimeMillis(),返回當前時間(并不考慮閏秒)
它可以和NTP同步,但是也會有對應的問題。如果本身時間點靠后,和NTP同步之后,會跳到之前的某一個時間點。這樣測量elapse time會出現(xiàn)問題(比如end-begin出現(xiàn)了負數(shù)時間)

4.1.2 單調(diào)時鐘

單調(diào)時鐘很適合測量時間間隔(即elapse time),在java中用System.nanoTime()
單個的單調(diào)時鐘的絕對值并沒有意義,可能是電腦啟動之后的納秒數(shù),可能是類似任意的東西。
并且,比較不同機器的單調(diào)時鐘并沒有意義,因為代表的東西不一樣(比如A時間代表A機器開機后的納秒數(shù),B時間代表B機器開機后的納秒數(shù),兩個開機時間又不一樣)

另外,單調(diào)時鐘也能根據(jù)NTP時間來調(diào)整前進后退的速率(注意不是直接調(diào)整時間,而是調(diào)整變動的速率),繼續(xù)保證單調(diào)性。

所以,在分布式系統(tǒng)中,使用單調(diào)時鐘測量經(jīng)過時間(例如超時)通常很好,因為它不假定不同節(jié)點的時鐘之間有任何同步,并且對測量的輕微不準確性不敏感。
(注意,這里說的也是同一個機器上的nanoTime來測量流逝的時間,不是不同機器的nanoTime來比較)

4.2 時鐘同步以及準確性

機器的時間可能并不如我們期望的那樣可靠

電腦的石英時鐘并不準確,可能取決于機器的溫度
如果電腦時鐘和NTP服務器差距太多,就會拒絕同步或者強制重置
由于防火墻等配置失誤,本地可能和NTP服務器失去同步
NTP同步也受到網(wǎng)絡延遲的影響,如果延遲巨大,NTP client也會放棄同步
等等

4.2.1 依賴同步時鐘

和上一節(jié)討論的不可靠的網(wǎng)絡一樣,時鐘也是不可靠的,即便大部分時間表現(xiàn)良好。
因此,如果自己的應用需要時間同步,那么必須小心監(jiān)控各個機器的時間。來避免某些節(jié)點的時間和其他大部分機器的時間相隔太大。

4.2.1.1 順序事件的時間戳

下面就有這個問題


圖2

順序如下

1.node1在本地時間42.004 寫x=1,備份給node 2和3
2.node 3在本地時間42.003 接收x=1,并且自增為2,備份給node1,2
3.node 2先收到了C的x=2的請求,攜帶的時間戳是42.003
4.node 2后收到了A的x=1的請求,攜帶的時間戳是42.004

這種沖突解決的方式在之前介紹到是LWW(last write wins),這里為了避免網(wǎng)絡延遲造成的影響,用client的時間戳而不是server的時間戳,但是兩個client的時間不同步(先發(fā)生的node1 本地時間是42.004而后發(fā)生的node
2本地時間是42.003),最終導致了錯誤的出現(xiàn)

那么能否讓NTP同步變得準確呢,避免這種錯誤的順序發(fā)生
不太可能,因為時間同步受限于"不可靠的網(wǎng)絡"

所謂的邏輯時鐘(logical clocks),基于遞增的計數(shù)器(而不是石英鐘),是對這種順序事件的一個更安全的選擇

4.2.1.2 時鐘讀數(shù)的置信區(qū)間

由于網(wǎng)絡的延遲,和NTP server同步也會變得不準確
因此把時鐘讀書當成一個時間點似乎并沒有意義,它更像一個有著置信區(qū)間的范圍

比如,一個系統(tǒng)可能有95%的信心,確信現(xiàn)在的時間在當前這個分鐘的10.3到10.5秒之間

4.2.1.3 全局快照的同步時鐘

這一節(jié)忽略吧

4.3 處理暫停

有多種原因使得線程暫停

1.如java之類的編程語言,會有GC使得縣城暫停
2.操作系統(tǒng)進行上下文切換,讓另外一個線程執(zhí)行了
3.應用執(zhí)行了同步的慢操作,如訪問磁盤等等

這些都會造成當前線程暫停(或者說處于等待)

分布式系統(tǒng)中的一個節(jié)點必須假定它的執(zhí)行可以在任何時刻暫停很長一段時間,即使在函數(shù)中間也是如此。
在暫停期間,世界其他地方一直在移動,甚至可能因為沒有響應而宣布暫停的節(jié)點已經(jīng)死機。
最終,暫停的節(jié)點可能會繼續(xù)運行,甚至沒有注意到它在之前一直處于睡眠狀態(tài)。

4.3.1 回復時間保證

有些軟件對于回復時間有嚴格的要求,如航天,汽車等領域。比如你不希望車禍時安全氣囊由于GC等原因延遲了釋放的時間。。。

這些系統(tǒng)有特定的“deadline”,表示程序必須在此之前響應。否則會認為整個系統(tǒng)失靈.

然而,這些系統(tǒng)會有大量額外的工作,并且有特定的語言,包,工具才能支持。
因此,構(gòu)建一個real-time的系統(tǒng)非常昂貴,并且經(jīng)常用于注重安全的嵌入式設備中。
并且,這種系統(tǒng)的可用性并不高,因為實時的嚴格要求,限制了吞吐量等。

對于大部分server端數(shù)據(jù)處理系統(tǒng),用實時的方式既不經(jīng)濟也不合適。
因此,這種非實時的系統(tǒng),必然會受到暫停的影響。

4.3.2 限制GC的影響

GC不能完全被阻止,但是可以有一些有效的手段來減少影響
比如有些語言對于GC的支持比較靈活,能夠跟蹤對象分配的頻率,剩余free內(nèi)存的大小等等
因此,當預計到一個節(jié)點需要GC時,把它從集群中抽出來(理解為當它掛了),不處理請求,等到GC結(jié)束后,重新加入集群處理請求即可。

5.總結(jié)

第二節(jié)講了單機出現(xiàn)的fault以及分布式的partial failures,設計分布式系統(tǒng)需要自行解決這些問題

第三節(jié)講了不可靠的網(wǎng)絡
由于網(wǎng)絡基于分組交換協(xié)議,因此有網(wǎng)絡阻塞,排隊等現(xiàn)象,會導致無限的延遲。
解決的方式就是超時,但是超時的時間如何界定,需要結(jié)合應用處理時長以及網(wǎng)絡情況決定

第四節(jié)講述不可靠的時間
區(qū)分了單調(diào)時間和Time-of-Day clock
講述了未同步的時鐘對應順序事件的影響
以及提出了時間讀數(shù)的置信區(qū)間的概念
再講解了暫停造成的影響以及從GC的角度講述了如何減小影響

6.名詞

fault, partial failures
(non) deterministic
shared-nothing system
asynchronous packet networks
timeout(超時)
(un)bounded delays
network congestion
flow control
packet-switched protocols(分組交換協(xié)議)
bursty traffic
Network Time Protocal(NTP)
logical clocks
deadline

思考

超時時間與網(wǎng)絡阻塞

記得之前有面試官問過,一個請求處理隊列太長了會怎么樣,太短了又會怎么樣

太長:排隊在后面的請求,等待時間長
太短:浪費計算機資源(假定1臺機器1s能處理1k個請求,現(xiàn)在同時來了1k個請求,每個機器處理隊列size只有100,那么代表需要幾乎10倍的機器數(shù)量來完成請求處理)

網(wǎng)絡超時時間的設定的影響,也比較類似

太短,可能會誤判,不僅誤判了某個節(jié)點掛掉,還導致請求重新被發(fā)送到其他節(jié)點,有可能造成請求被消費兩次
太長,等待時間長,影響效率

動態(tài)的設置超時時間

文中列舉的系統(tǒng)沒有了解過。
不過zk上是配置常量的時間,一個超時檢測時間應該是
周期時間(如5s) * 超時檢測周期數(shù)(如4)

TCP連接和電話網(wǎng)絡的區(qū)別

前者利用帶塊不固定,后者固定

TCP和電話網(wǎng)絡的例子

這一段比較長見識,一些段落直接谷歌翻譯的

java中 System.nanoTime()和System.currentTimeMillis()區(qū)別

第一次知道這倆的區(qū)別,高科技。。。
前者單調(diào),但是僅用來測量過去的時間,后者反應當前時間,但是不保證單調(diào)
https://stackoverflow.com/questions/351565/system-currenttimemillis-vs-system-nanotime

還特意回看了一下,zk的源碼里面,兩種方式都有用到

LWW的缺陷在哪

結(jié)合圖2
如果說按照統(tǒng)一到達目標server的時間,可以通過單調(diào)時鐘解決“不可靠的時鐘”問題,但是,可能由于網(wǎng)絡延遲,導致實際上先發(fā)送的請求卻后到達
如果說避免網(wǎng)絡延遲,按照發(fā)送方server的時間來判斷請求的先后順序,可能由于多個發(fā)送方server時間不同步,造成實際上先發(fā)送的請求"看起來"是后發(fā)送的(圖2即這個例子)

7.refer

http://www.itdecent.cn/p/0a34337445e2

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容