從本文開始,將記錄做者學習 MIT 6.824 分佈式系統的學習筆記,若是有志同道合者,歡迎一塊兒交流。數據庫
RPC 全稱爲 Remote Procedure Call,他表示一種遠程過程的調用,他可讓用戶在不知道底層網絡協議的狀況下,在本地的計算機就能夠調用遠端服務器的一些處理方法,而後服務器會把處理的結果返回到用戶本地,就像這個方法寫在用戶本地空間中同樣。緩存
RPC 的層級結構以下所示:服務器
如上圖所示,RPC 的操做流程爲:網絡
RPC 具體實現的關鍵技術點以下多線程
RPC 的潛在失敗狀況有以下幾種:併發
對此,爲了必定要讓客戶端拿處處理結果,有如下幾種解決方案:分佈式
該方法的中心思想是:client 發送等待 server 回答,若是沒收到,則繼續發送。該方式可能形成的問題有:函數
問題一:可能客戶端 -> 服務端的網絡一直相同,服務器處理正常,可是在回覆過程出現問題,從而形成客戶端持續發送請求,使得客戶端一直重複處理,浪費資源。學習
問題二:若是要處理的請求具備事務性,如操做數據庫,那麼使用這種方案就沒法保證先後的一致性。考慮以下的狀況,假設咱們要操做的對象是服務端的數據庫,可能會出現以下的問題:編碼
如上圖所示,PUT 表示咱們要去修改一個 key 對應的值,而 GET 則讓咱們獲取這個 key 對應的值,所以當咱們延遲的請求到達服務器時,服務器已經處理其餘的請求,形成咱們最後一次 GET 方法獲取的結果可能與咱們的預期不一樣。
因此 At least once 會形成資源浪費,服務端出現不一致的狀況,不是一個很好的方法
其實,方案一的問題在於服務端沒法判斷客戶端由於各類緣由收不到而重複發出的請求,所以咱們須要讓服務器能夠發現以前的重複請求,而且在不執行 handler 的狀況下,返回以前緩存的結果。所以咱們的請求須要帶上惟一的識別碼,這個識別碼能夠經過 client ip + 時間戳 生成,也可使用 client ip + seq(序列號)。這樣,服務端就能夠知道這是否是一個已經處理過的請求,並經過緩存直接返回結果。
然而,這對服務端也提出了新的問題:
問題一: 何時能夠刪掉這些緩存?
畢竟服務端不可能永遠保存這些緩存,這會形成容量不夠的問題。答案其實也很簡單,那就是客戶端證實本身收到了回覆的時候。對此,咱們 RPC 用戶端協議能夠帶一些附加信息,好比:
對於客戶端 RPC 請求,能夠帶上其近期已經收到回覆的序列號,這樣,服務端的在解碼過程當中能夠經過解析該字段,從而刪除已經肯定收到回覆的緩存
能夠經過使用 sequence number 的方法,即客戶端的該次請求是上一次請求的序列號 + 1。這樣,客戶端就能夠在請求中帶上一個序列號 n,表示 seq < n 的請求我都已經收到了。服務端便可刪除全部序列號小於 n 的緩存。以下的示意圖展現了這一種方法:
問題二:若是一個請求仍在處理,此時"心急的"客戶端又發了一個相同的請求,該如何處理?
這個時候,服務端能夠維護一個關於"處理中"的緩存,將正在處理的 request 記錄在該緩存,若是有相同請求到來,則先去查詢"處理中"的緩存,再去查找"已完成"的緩存,若是存在,則捨棄這條請求,返回已有結果或者等待原有的相同請求處理完成並返回。
本章是對 MIT 6.824 第二課 RPC 內容的總結,主要講了 RPC 的結構,以及一些潛在錯誤的處理方法,爲 lab1 中 mapreduce RPC 的實現打下基礎。