重生之我要學習分布式系列第一篇。
課程首頁:
明星課程 MIT 6.5840: Distributed Systems,原名 6.824,在 2024 年一共布置了 5 個 lab。這幾個 lab 循序漸進,從古法分布式 MapReduce 搓起,然後手搓 raft,並最終造出類似 TiKV 的分布式數據庫,整個過程做下來真有種開局一根針手搓 Windows 的爽感。
如何防治勁椎病#
就像某國產 3A 大作的九九八十一難的第一難是解壓遊戲,本課程的第一難是讓人憋出勁椎病的文檔網頁。
由於文檔彈道偏左,考慮在控制台裡把它水平居中。
const body = document.body;
body.style.maxWidth = "800px";
body.style.marginLeft = "auto";
body.style.marginRight = "auto";
const elements = body.getElementsByTagName("*");
for (let i = 0; i < elements.length; i++) {
elements[i].style.maxWidth = "100%";
elements[i].style.boxSizing = "border-box";
}
lab 內容簡單介紹#
lab1 是比較獨立的開胃小菜,要實現一個 Google 曾經使用的 MapReduce 框架。
lab2 實現一個簡單的單體 KV 伺服器,要求在網路不穩定的情況下也能保證服務不出錯。
lab3 則是實現 Raft 協議。
lab4 要基於 lab3 實現的 Raft 協議,構建一個 KV 伺服器集群,也就是在 lab2 的基礎上用 Raft 協議保證 Fault-tolerant。
lab5 則要在 lab4 的基礎上,把數據庫的數據分為幾個分片(shard)來減輕單個伺服器負擔,需要實現他們的數據遷移。
個人認為最難實現的兩個 lab 顯然是 lab3 和 lab5。lab3 需要對 Raft 的原始論文仔細研讀,lab5 則需要一開始就設計好整個架構如何實現,輔以網上搜索相關的實現思路才好開始上手。
Debug 小技巧#
個人認為實現這些 lab 最有挑戰性的地方不是在設計或者編寫上,而是 debug 上。因為傳統的打斷點 debug 方法是對一個單體應用操作,而本次 lab 需要面對的是一個集群系統,打斷點的方法就很難操作,所以主要還是靠 print 大法。那么設計一個好的打印函數非常有必要,例如可以把打印函數抽象出來。例如 lab5 我用了這樣一個函數對 println 包裝
func (kv *ShardKV) SSPrintf(format string, a ...interface{}) (n int, err error) {
if SDebug {
log.Printf("[server %d]"+format, append([]interface{}{kv.gid}, a...)...)
}
return
}
kv.gid 是標識 ShardKV 的相關信息之一,實踐上還可以塞入更多的基本信息,當然這可能要考慮獲取這些信息會不會造成讀寫上的條件競爭的問題。
本次實驗我只用了 SDebug 來判斷是否打印日誌,但實際上可以設計一個類似 level 的東西,來方便區分重要日誌和不那麼重要的日誌。
在需要分析 Bug 日誌的時候,可以借助 Linux 的 tee 命令讓日誌同時輸出到 stdout 和文件裡。
go test -race -run TestConcurrent3_5B | tee bug.txt
在需要輸出日誌的地方可以多加幾個條件判斷,確認是 bug 可能發生的那種條件,這樣可以減少大量無意義的日誌。
代碼倉庫#
KingBridgeSS/MIT-6.5840: My solution for labs in MIT-6.5840 (https://pdos.csail.mit.edu/6.824/)