MIT 6.824 學習筆記(一)--- RPC 詳解

從本文開始,將記錄做者學習 MIT 6.824 分佈式系統的學習筆記,若是有志同道合者,歡迎一塊兒交流。數據庫

RPC 的定義和結構

RPC 全稱爲 Remote Procedure Call,他表示一種遠程過程的調用,他可讓用戶在不知道底層網絡協議的狀況下,在本地的計算機就能夠調用遠端服務器的一些處理方法,而後服務器會把處理的結果返回到用戶本地,就像這個方法寫在用戶本地空間中同樣。緩存

RPC 的層級結構以下所示:服務器

RPC structure

  • Client 端(由上到下):
    • Application:應用層,表示和用戶直接交互的部分,用戶在該層肯定本身想要調用的函數,而且輸入參數
    • stub:原意是菸蒂,很形象的表示這只是個函數空殼,表示用戶想要調用的函數,這裏能夠是函數名
    • RPC lib:RPC 庫,包括一系列的編碼解碼工做,對於用戶端來講,是編碼用戶的函數調用請求進入網絡層,或者是解碼服務端的結果成爲上層結構能夠理解的語言
    • Network Layer:網絡層傳輸協議,這裏再也不贅述,它可使 TCP
  • Server 端(由下到上):
    • Network Layer:網絡層傳輸協議,同上
    • RPC lib:RPC 庫,對服務端來講,用於解碼用戶的調用函數請求,或者是函數結果的封裝。
    • Dispatcher:用戶的請求到達該層後,服務端須要經過 Dispatcher,根據請求的函數,找到服務器中對應的函數,並把任務交到這個函數上
    • Handler:具體的函數邏輯

如上圖所示,RPC 的操做流程爲:網絡

  • 用戶端寫好 function call,向下傳遞到網絡層併發送到服務端
  • 服務端上行用戶請求,經過 Dispatcher 找到具體的 Handler,由 Handler 處理具體的邏輯
  • 服務端處理完畢後,經過 RPC lib 編碼發還到客戶端
  • 客戶端通過解碼後上行結果,用戶得到 Application 層得到結果返回。

RPC 的關鍵技術點

RPC 具體實現的關鍵技術點以下多線程

  • Marshall / Unmarshall:即爲咱們日常說的序列化/反序列化,用於將應用層的信息轉化爲網絡層能夠理解語言,抑或是將網絡層的信息解碼成爲應用層能夠理解的語言。這部分在 RPC lib 中能夠完成
  • Binding:在一個大的網絡環境中,如何爲用戶分配一個符合用戶所需邏輯要求的,可用的服務器也是一大難點
  • Threads:handler 可能執行得很慢,而經過多線程則能夠加強 Server 的吞吐量

RPC 的潛在失敗狀況和處理方案

RPC 的潛在失敗狀況有以下幾種:併發

  • Lose Packet:網絡環境差形成的丟包
  • Broken Net:網絡直接斷了
  • Server Crash:服務器崩了
  • Server Slow:服務器處理速度太慢

對此,爲了必定要讓客戶端拿處處理結果,有如下幾種解決方案:分佈式

方案一:At Least once:

該方法的中心思想是:client 發送等待 server 回答,若是沒收到,則繼續發送。該方式可能形成的問題有:函數

  • 問題一:可能客戶端 -> 服務端的網絡一直相同,服務器處理正常,可是在回覆過程出現問題,從而形成客戶端持續發送請求,使得客戶端一直重複處理,浪費資源。學習

    at least once1

  • 問題二:若是要處理的請求具備事務性,如操做數據庫,那麼使用這種方案就沒法保證先後的一致性。考慮以下的狀況,假設咱們要操做的對象是服務端的數據庫,可能會出現以下的問題:編碼

    at least once2

    如上圖所示,PUT 表示咱們要去修改一個 key 對應的值,而 GET 則讓咱們獲取這個 key 對應的值,所以當咱們延遲的請求到達服務器時,服務器已經處理其餘的請求,形成咱們最後一次 GET 方法獲取的結果可能與咱們的預期不一樣。

因此 At least once 會形成資源浪費,服務端出現不一致的狀況,不是一個很好的方法

方案二:At Most Once

其實,方案一的問題在於服務端沒法判斷客戶端由於各類緣由收不到而重複發出的請求,所以咱們須要讓服務器能夠發現以前的重複請求,而且在不執行 handler 的狀況下,返回以前緩存的結果。所以咱們的請求須要帶上惟一的識別碼,這個識別碼能夠經過 client ip + 時間戳 生成,也可使用 client ip + seq(序列號)。這樣,服務端就能夠知道這是否是一個已經處理過的請求,並經過緩存直接返回結果。

然而,這對服務端也提出了新的問題:

  • 問題一: 何時能夠刪掉這些緩存?

    畢竟服務端不可能永遠保存這些緩存,這會形成容量不夠的問題。答案其實也很簡單,那就是客戶端證實本身收到了回覆的時候。對此,咱們 RPC 用戶端協議能夠帶一些附加信息,好比:

    • 對於客戶端 RPC 請求,能夠帶上其近期已經收到回覆的序列號,這樣,服務端的在解碼過程當中能夠經過解析該字段,從而刪除已經肯定收到回覆的緩存

    • 能夠經過使用 sequence number 的方法,即客戶端的該次請求是上一次請求的序列號 + 1。這樣,客戶端就能夠在請求中帶上一個序列號 n,表示 seq < n 的請求我都已經收到了。服務端便可刪除全部序列號小於 n 的緩存。以下的示意圖展現了這一種方法:

      at_most_once

  • 問題二:若是一個請求仍在處理,此時"心急的"客戶端又發了一個相同的請求,該如何處理?

    這個時候,服務端能夠維護一個關於"處理中"的緩存,將正在處理的 request 記錄在該緩存,若是有相同請求到來,則先去查詢"處理中"的緩存,再去查找"已完成"的緩存,若是存在,則捨棄這條請求,返回已有結果或者等待原有的相同請求處理完成並返回。

總結

本章是對 MIT 6.824 第二課 RPC 內容的總結,主要講了 RPC 的結構,以及一些潛在錯誤的處理方法,爲 lab1 中 mapreduce RPC 的實現打下基礎。

相關文章
相關標籤/搜索