當前位置: 妍妍網 > 碼農

這次,帶大家造個輪子!

2024-03-09碼農

大家好,我是程式設計師魚皮。

年前剛剛做完 的教程,現在我又要出新計畫了。

之前做的計畫基本都是和業務相關的,但這次我要帶大家做一個輪子類別計畫 —— 從 0 到 1 開發 RPC 框架。

這個計畫的教程魚皮已經寫完了,3 月 15 號前會全部釋出。

為什麽要帶做這個計畫?

很多同學聽到 「開發框架」 可能會有點膽怯,但其實開發 RPC 框架並不難, 最多幾個小時 就能學會核心流程!能夠快速給簡歷增加一個區別於增刪改查的框架計畫。

最主要的是,開發 RPC 框架涉及很多常用的技術知識點、還能學習到很多架構設計方面的思路和技巧。

因此,強烈建議所有後端方向的同學,動手做個自己的 RPC 框架!

值得一提的是,我在決定做這個計畫前,已經完整地看了好幾套網上主流的 RPC 框架教程,但或多或少有些我覺得沒講明白的地方、或者有更好的實作方式。所以為了幫助大家更快學習,我還是決定用自己的方式做套教程。

當然,這也是我原創計畫教程系列的一部份。

魚皮原創計畫教程系列:https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd

本計畫程式碼開源:https://github.com/liyupi/yu-rpc

這篇文章,我就先給大家分享一下 RPC 的基本概念,以及 RPC 框架的設計實作思路。感興趣的同學可以 學習,也可以自己閱讀源碼學習。

一、基本概念

什麽是 RPC?

專業定義:RPC(Remote Procedure Call)即遠端程序呼叫,是一種電腦通訊協定,它允許程式在不同的電腦之間進行通訊和互動,就像本地呼叫一樣。

簡單理解,新開了一家賣魚皮的熟食店,現在你作為消費者想要把魚皮購買到家。如果是以前,你只能自己跑腿到線下店鋪購買,耗時耗力。但現在有了手機、網路、外賣平台,你只需要在家動動手指,就能點個外賣讓騎手把魚皮配送到家,你不需要關註網路是怎麽傳輸的、外賣平台是怎麽操作的、騎手小哥是怎麽配送的,只負責享受魚皮就行了。

為什麽需要 RPC?

回到 RPC 的概念,RPC 允許一個程式(稱為服務消費者)像呼叫自己程式的方法一樣,呼叫另一個程式(稱為服務提供者)的介面,而不需要了解數據的傳輸處理過程、底層網路通訊的細節等。這些都會由 RPC 框架幫你完成,使得開發者可以輕松呼叫遠端服務,快速開發分布式系統。

舉個例子,現在有個計畫 A 提供了點餐服務,計畫 B 需要呼叫點餐服務完成下單。

點餐服務和介面的範例虛擬碼如下:

interfaceOrderService{
// 點餐,返回 orderId
longorder(參數1, 參數2, 參數3);
}

如果沒有 RPC 框架,計畫 B 怎麽呼叫計畫 A 的服務呢?

首先,由於計畫 A 和計畫 B 都是獨立的系統,不能像 SDK 一樣作為依賴包引入。那麽就需要計畫 A 提供 web 服務,並且編寫一個點餐介面暴露服務,比如存取 http://yupi.icu 就能呼叫點餐服務;然後計畫 B 作為服務消費者,需要自己構造請求,並透過 HttpClient 請求上述地址。如果計畫 B 需要呼叫更多第三方服務,每個服務和方法的呼叫都編寫一個 HTTP 請求,那麽會非常麻煩!

範例虛擬碼如下:

url = "http://yupi.icu"
req = new Req(參數1, 參數2, 參數3)
res = httpClient.post(url).body(req).execute()
orderId = res.data.orderId

而有了 RPC 框架,計畫 B 可以透過一行程式碼完成呼叫!

範例虛擬碼如下:

orderId = orderService.order(參數1, 參數2, 參數3)

看起來就跟呼叫自己計畫的方法沒有任何區別!是不是很絲滑?

二、RPC 框架實作思路

基本設計

RPC 框架為什麽能幫我們簡化呼叫?如何實作一個 RPC 框架呢?

其實很簡單,開局一張圖,有服務消費者和服務提供者兩個角色:

消費者想要呼叫提供者,就需要提供者啟動一個 web 服務 ,然後透過 請求客戶端 發送 HTTP 或者其他協定的請求來呼叫。

比如請求 yupi.icu/order 地址後,提供者會呼叫 orderService 的 order 方法:

但如果提供者提供了多個服務和方法,每個介面和方法都要單獨寫一個介面?消費者要針對每個介面寫一段 HTTP 呼叫的邏輯麽?

其實可以提供一個統一的服務呼叫介面,透過 請求處理器 根據客戶端的請求參數來進行不同的處理、呼叫不同的服務和方法。

可以在服務提供者程式維護一個 本地服務註冊器 ,記錄服務和對應實作類的對映。

舉個例子,消費者要呼叫 orderService 服務的 order 方法,可以發送請求,參數為 service=orderService,method=order ,然後請求處理器會根據 service 從服務註冊器中找到對應的服務實作類,並且透過 Java 的反射機制呼叫 method 指定的方法。

需要註意的是,由於 Java 物件無法直接在網路中傳輸,所以要對傳輸的參數進行 序列化 反序列化

為了簡化消費者發請求的程式碼,實作類似本地呼叫的體驗。可以基於代理模式,為消費者要呼叫的介面生成一個代理物件,由代理物件完成請求和響應的過程。

所謂代理,就是有人幫你做一些事情,不用自己操心。

至此,一個最簡易的 RPC 框架架構圖誕生了:

上圖中的虛線框部份,就是 RPC 框架需要提供的模組和能力。

擴充套件設計

雖然上述設計已經跑通了基本呼叫流程,但離一個完備的 RPC 框架還有很大的差距,讓我們帶著問題來進一步完善下架構設計。

1、服務註冊發現

問題 1:消費者如何知道提供者的呼叫地址呢?

類比生活場景,我們點外賣時,外賣小哥如何知道我們的地址和店鋪的地址?肯定是買家和賣家分別填寫地址,由平台來保存的。因此,我們需要一個 註冊中心 ,來保存服務提供者的地址。消費者要呼叫服務時,只需從註冊中心獲取對應服務的提供者地址即可。

架構圖如下:

一般用現成的第三方註冊中心,比如 Redis、Zookeeper 即可。

2、負載均衡

問題 2:如果有多個服務提供者,消費者應該呼叫哪個服務提供者呢?

我們可以給服務呼叫方增加負載均衡能力,透過指定不同的演算法來決定呼叫哪一個服務提供者,比如輪詢、隨機、根據效能動態呼叫等。

架構圖如下:

3、容錯機制

問題 3:如果服務呼叫失敗,應該如何處理呢?

為了保證分布式系統的高可用,我們通常會給服務的呼叫增加一定的容錯機制,比如失敗重試、降級呼叫其他介面等等。

架構圖如下:

4、其他

除了上面幾個經典設計外,如果想要做一個優秀的 RPC 框架,還要考慮很多問題。

比如:

  • 服務提供者下線了怎麽辦?需要一個失效節點剔除機制。

  • 服務消費者每次都從註冊中心拉取資訊,效能會不會很差?可以使用緩存來最佳化效能。

  • 如何最佳化 RPC 框架的傳輸通訊效能?比如選擇合適的網路框架、自訂協定頭、節約傳輸體積等。

  • 如何讓整個框架更利於擴充套件?比如使用 Java 的 SPI 機制、配置化等等。

  • 所以,完成 RPC 計畫並不難,但做一個完美的 RPC 計畫卻是難於上青天啊!


    總結一下,我們可以透過做一個 RPC 計畫學習到網路、序列化、代理、服務註冊發現、負載均衡、容錯、可延伸設計等知識,相信完成計畫後會收獲滿滿。

    最後,感興趣的同學,可以存取:https://yupi.icu,加入編程導航學習所有魚皮的原創計畫~
    👇🏻 點選下方閱讀原文,獲取魚皮往期編程幹貨。

    往期推薦