摘要:一旦有一方改變,要及時通知對方,否則就會出現(xiàn)問題。對于,主要處理高性能的傳輸,以及網(wǎng)絡(luò)的錯誤和異常。這個框架是在協(xié)議中使用的。就是網(wǎng)絡(luò)文件系統(tǒng)。唯一標(biāo)識請求和回復(fù)。
【前五篇】系列文章傳送門:
網(wǎng)絡(luò)協(xié)議 14 - 流媒體協(xié)議:要說愛你不容易
網(wǎng)絡(luò)協(xié)議 15 - P2P 協(xié)議:小種子大學(xué)問
網(wǎng)絡(luò)協(xié)議 16 - DNS 協(xié)議:網(wǎng)絡(luò)世界的地址簿
網(wǎng)絡(luò)協(xié)議 17 - HTTPDNS:私人定制的 DNS 服務(wù)
網(wǎng)絡(luò)協(xié)議 18 - CDN:家門口的小賣鋪
????這幾年微服務(wù)很火,想必各位博友或多或少的都接觸過。微服務(wù)概念中,
各服務(wù)間的相互調(diào)用是不可或缺的一環(huán)。你知道微服務(wù)之間是通過什么方式相互調(diào)用的嗎?
????你可能說,這還不簡單,用 socket 唄。服務(wù)之間分調(diào)用方和被調(diào)用方,我們就建立一個 TCP 或者 UDP 連接進行通信就好了。
????說著說著,你可能就會發(fā)現(xiàn),這事兒沒那么簡單。
????我們就拿最簡單的場景:
客戶端調(diào)用一個加法函數(shù),將兩個整數(shù)加起來,返回它們的和。
????如果放在本地調(diào)用,那是簡單的不能再簡單,但是一旦變成了遠程調(diào)用,門檻一下子就上去了。
????首先,你要會 socket 編程,至少要先了解咱們這個系列的所有協(xié)議 ,然后再看 N 本磚頭厚的 socket 程序設(shè)計的書,學(xué)會咱們了解過的幾種 socket 程序設(shè)計的模型。
????這就使得本來大學(xué)畢業(yè)就能干的一項工作,變成了一件五年工作經(jīng)驗都不一定干好的工作,而且,搞定了 socket 程序設(shè)計,才是萬里長征的第一步,后面還有很多問題呢。
存在問題問題一:如何規(guī)定遠程調(diào)用的語法?
????客戶端如何告訴服務(wù)端,我是一個加法,而另一個是減法。是用字符串 “add” 傳給你,還是傳給你一個整數(shù),比如 1 表示加法,2 表示減法?
????服務(wù)端又該如果告訴客戶端,我這個是加法,目前只能加整數(shù),不能加小數(shù)和字符串。而另一個加法 “add1”,它能實現(xiàn)小數(shù)和整數(shù)的混合加法,那返回值是什么?正確的時候返回什么,錯誤的時候又返回什么?
問題二:如何傳遞參數(shù)?
????是先傳兩個整數(shù),后傳一個操作數(shù) “add”,還是先傳操作符,再傳兩個整數(shù)?
????另外,如果我們是用 UDP 傳輸,把參數(shù)放在一個報文里還好,但如果是 TCP,是一個流,在這個流里面如何區(qū)分前后兩次調(diào)用?
問題三:如何表示數(shù)據(jù)?
????在我們的加法例子中,傳遞的就是一個固定長度的 int 值,這種情況還好,如果是變長的類型,是一個結(jié)構(gòu)體,甚至是一個類,應(yīng)該怎么辦呢?即使是 int,在不同的平臺上長度也不同,該怎么辦呢?
問題四:如何知道一個服務(wù)端都實現(xiàn)了哪些遠程調(diào)用?從哪個端口可以訪問這個遠程調(diào)用?
????假設(shè)服務(wù)端實現(xiàn)了多個遠程調(diào)用,每個實現(xiàn)可能都不在一個進程中,監(jiān)聽的端口也不一樣,而且由于服務(wù)端都是自己實現(xiàn)的,不可能使用一個大家都公認(rèn)的端口,而且有可能多個進程部署在一臺機器上,大家需要搶占端口,為了防止沖突,往往使用隨機端口,那客戶端如何找到這些監(jiān)聽的端口呢?
問題五:發(fā)生了錯誤、重傳、丟包、性能等問題怎么辦?
????本地調(diào)用沒有這個問題,但是一旦到網(wǎng)絡(luò)上,這些問題都需要處理,因為網(wǎng)絡(luò)是不可靠的,雖然在同一個連接中,我們還可以通過 TCP 協(xié)議保證丟包、重傳的問題,但是如果服務(wù)器崩潰了又重啟,當(dāng)前連接斷開了,TCP 就保證不了了,需要應(yīng)用自己進行重新調(diào)用,重新傳輸會不會同樣的操作做兩遍,遠程調(diào)用性能會不會受影響呢?
????看到這么多問題,是不是很頭疼?還記得咱們了解 http 的時候,認(rèn)識的協(xié)議三要素嗎?
????本地調(diào)用函數(shù)里很多問題,比如詞法分析、語法分析、語義分析等待,這些問題編譯器基本上都幫我們解決了,但是在遠程調(diào)用中,這些問題我們都要自己考慮。
協(xié)議約定問題????很多公司對于這個問題,是弄一個核心通信組,里面都是 socket 編程的大牛,實現(xiàn)一個統(tǒng)一的庫,讓其他業(yè)務(wù)組的人來調(diào)用,業(yè)務(wù)的人不需要知道中間傳輸?shù)募?xì)節(jié)。
????通信雙方的語法、語義、格式、端口、錯誤處理等,都需要調(diào)用方和被調(diào)用方開會商量,雙方達成一致。一旦有一方改變,要及時通知對方,否則就會出現(xiàn)問題。
????但是,不是每個公司都能通過這種大牛團隊解決問題的,而是使用已經(jīng)實現(xiàn)好的框架。
????有一個大牛(Bruce Jay Nelson)通過一篇論文,定義了 RPC 的調(diào)用標(biāo)準(zhǔn)。后面所有 RPC 框架都是按照這個標(biāo)準(zhǔn)模式來的。
整個過程如下:
客戶端的應(yīng)用想發(fā)起一個遠程調(diào)用時,它實際上是通過本地調(diào)用方的 Stub。它負(fù)責(zé)將調(diào)用的接口、方法和參數(shù),通過約定的協(xié)議規(guī)范進行編碼,并通過本地 RPCRuntime 進行傳輸,將調(diào)用網(wǎng)絡(luò)包發(fā)送到服務(wù)器;
服務(wù)端的 RPCRuntime 收到請求后,交給提供方 Stub 進行編碼,然后調(diào)用服務(wù)端的方法,獲取結(jié)果,并將結(jié)果編碼后,發(fā)送給客戶端;
客戶端的 RPCRuntime 收到結(jié)果,發(fā)給調(diào)用方 Stub 解碼得到結(jié)果,返回給客戶端。
????上面過程中分了三個層次:客戶端、Stub 層、服務(wù)端。
????對于客戶端和服務(wù)端,都像是本地調(diào)用一樣,專注于業(yè)務(wù)邏輯的處理就可以了。對于 Stub 層,處理雙方約定好的語法、語義、封裝、解封裝。對于 RPCRuntime,主要處理高性能的傳輸,以及網(wǎng)絡(luò)的錯誤和異常。
????最早的 RPC 的一種實現(xiàn)方式稱為 Sun RPC 或 ONC RPC。Sun 公司是第一個提供商業(yè)化 RPC 庫和 RPC 編譯器的公司。這個 RPC 框架是在 NFS 協(xié)議中使用的。
????NFS(Network File System)就是網(wǎng)絡(luò)文件系統(tǒng)。要使 NFS 成功運行,就要啟動兩個服務(wù)端,一個 mountd,用來掛載文件路徑。另一個是 nfsd,用來讀寫文件。NFS 可以在本地 mount 一個遠程的目錄到本地目錄,從而實現(xiàn)讓本地用戶在本地目錄里面讀寫文件時,操作是是遠程另一臺機器上的文件。
????遠程操作和遠程調(diào)用的思路是一樣的,就像本地操作一樣,所以 NFS 協(xié)議就是基于 RPC 實現(xiàn)的。當(dāng)然,無論是什么 RPC,底層都是 socket 編程。
????XDR(External Data Representation,外部數(shù)據(jù)表示法)是有一個標(biāo)準(zhǔn)的數(shù)據(jù)壓縮格式,可以表示基本的數(shù)據(jù)類型,也可以表示結(jié)構(gòu)體。
????這里有幾種基本的數(shù)據(jù)類型。
????在 RPC 的調(diào)用過程中,所有的數(shù)據(jù)類型都要封裝成類似的格式,而且 RPC 的調(diào)用和結(jié)果返回也有嚴(yán)格的格式。
XID 唯一標(biāo)識請求和回復(fù)。請求是 0,回復(fù)是 1;
RPC 有版本號,兩端要匹配 RPC 協(xié)議的版本號。如果不匹配,就會返回 Deny,原因是 RPC_MISMATCH;
程序有編號。如果服務(wù)端找不到這個程序,就會返回 PROG_UNAVAIL;
程序有版本號。如果程序的版本號不匹配,就會返回 PROG_MISMATCH;
一個程序可以有多個方法,方法也有編號,如果找不到方法,就會返回 PROG_UNAVAIL;
調(diào)用需要認(rèn)證鑒權(quán),如果不通過,返回 Deny;
最后是參數(shù)列表,如果參數(shù)無法解析,返回 GABAGE_ARGS;
????為了可以成功調(diào)用 RPC,在客戶端和服務(wù)端實現(xiàn) RPC 的時候,首先要定義一個雙方都認(rèn)可的程序、版本、方法、參數(shù)等。
????對于上面的加法而言,雙方約定為一個協(xié)議定義文件,同理,如果是 NFS、mount 和讀寫,也會有類似的定義。
????有了協(xié)議定義文件,ONC RPC 會提供一個工具,根據(jù)這個文件生成客戶端和服務(wù)器端的 Stub 程序。
????最下層的是 XDR 文件,用于編碼和解碼參數(shù)。這個文件是客戶端和服務(wù)端共享的,因為只有雙方一致才能成功通信。
????在客戶端,會調(diào)用 clnt_create 創(chuàng)建一個連接,然后調(diào)用 add_1,這是一個 Stub 函數(shù),感覺是在調(diào)用本地函數(shù)一樣。其實是這個函數(shù)發(fā)起了一個 RPC 調(diào)用,通過調(diào)用 clnt_call 來調(diào)用 ONC RPC 的類庫,來真正發(fā)送請求。調(diào)用的過程較為復(fù)雜,后續(xù)再進行專門的說明。
????當(dāng)然,服務(wù)端也有一個 Stub 程序,監(jiān)聽客戶端的請求,當(dāng)調(diào)用到達的時候,判斷如果是 add,則調(diào)用真正的服務(wù)端邏輯,也就是將兩個數(shù)加起來。
????服務(wù)端將結(jié)果返回服務(wù)端的 Stub,Stub 程序發(fā)送結(jié)果給客戶端 Stub,客戶端 Stub 收到結(jié)果后就返回給客戶端的應(yīng)用程序,從而完成這個調(diào)用過。
????有了這個 RPC 框架,前面五個問題中的 “如何規(guī)定遠程調(diào)用的語法?”、“如何傳遞參數(shù)?” 以及 “如何表示數(shù)據(jù)?” 基本解決了,這三個問題我們統(tǒng)稱為協(xié)議約定問題。
傳輸問題????前三個問題解決了,但是錯誤、重傳、丟包以及性能問題還沒有解決,這些問題我們統(tǒng)稱為傳輸問題。這個 Stub 層就無能為力了,而是由 ONC RPC 的類庫來實現(xiàn)。
????在這個類庫中,為了解決傳輸問題,對于每一個客戶端,都會創(chuàng)建一個傳輸管理層,而每一次 RPC 調(diào)用,都會是一個任務(wù),在傳輸管理層,你可以看到熟悉的隊列機制、擁塞窗口機制等。
????由于在網(wǎng)絡(luò)傳輸?shù)臅r候,經(jīng)常需要等待,而同步的方式往往效率比較低,因而也就有 socket 的異步模型。
????為了能夠異步處理,對于遠程調(diào)用的處理,往往是通過狀態(tài)機來實現(xiàn)的。只有當(dāng)滿足某個狀態(tài)的時候,才進行下一步,如果不滿足狀態(tài),不是在那里等待,而是將資源留出來,用來處理其他的 RPC 調(diào)用。
????如上圖,從圖也可以看出,這個狀態(tài)轉(zhuǎn)換圖還是很復(fù)雜的。
????首先,進入起始狀態(tài),查看 RPC 的傳輸層隊列中有沒有空閑的位置,可以處理新的 RPC 任務(wù),如果沒有,說明太忙了,直接結(jié)束或重試。如果申請成功,就可以分配內(nèi)存,獲取服務(wù)端的端口號,然后連接服務(wù)器。
????連接的過程要有一段時間,因而要等待連接結(jié)果,如果連接失敗,直接結(jié)束或重試。如果連接成功,則開始發(fā)送 RPC 請,然后等待獲取 RPC 結(jié)果。同樣的,這個過程也需要時間,如果發(fā)送出錯,就重新發(fā)送,如果連接斷開,要重新連接,如果超時,要重新傳輸。如果獲取到結(jié)果,就可以解碼,正常結(jié)束。
????這里處理了連接失敗、重試、發(fā)送失敗、超時、重試等場景,因而實現(xiàn)一個 RPC 框架,其實很有難度。
服務(wù)發(fā)現(xiàn)問題????傳輸問題解決了,我們還遺留了一個 “如何找到 RPC 服務(wù)端的那個隨機端口”,這個問題我們稱為服務(wù)發(fā)現(xiàn)問題,在 ONC RPC 中,服務(wù)發(fā)現(xiàn)是通過 portmapper 實現(xiàn)的。
????portmapper 會啟動在一個眾所周知的端口上,RPC 程序由于是用戶自己寫的,會監(jiān)聽在一個隨機端口上,但是 RPC 程序啟動的時候,會向 portmapper 注冊。
????客戶端要訪問 RPC 服務(wù)端這個程序的時候,首先查詢 portmapper,獲取 RPC 服務(wù)端程序的隨機端口,然后向這個隨機端口建立連接,開始 RPC 調(diào)用。
從下圖中可以看出,mount 命令的 RPC 調(diào)用就是這樣實現(xiàn)的。
小結(jié)遠程調(diào)用看起來用 socket 編程就可以了,其實是很復(fù)雜的,要解決協(xié)議約定問題、傳輸問題和服務(wù)發(fā)現(xiàn)問題;
ONC RPC 框架以及 NFS 的實現(xiàn),給出了解決上述三大問題的示范性實現(xiàn),也就是公用協(xié)議描述文件,并通過這個文件生成 Stub 程序。RPC 的傳輸一般需要一個狀態(tài)機,需要另外一個進程專門做服務(wù)發(fā)現(xiàn)。
參考:
劉超-趣談網(wǎng)絡(luò)協(xié)議系列課;
如何給老婆解釋什么是RPC;
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/72853.html
摘要:一旦有一方改變,要及時通知對方,否則就會出現(xiàn)問題。對于,主要處理高性能的傳輸,以及網(wǎng)絡(luò)的錯誤和異常。這個框架是在協(xié)議中使用的。就是網(wǎng)絡(luò)文件系統(tǒng)。唯一標(biāo)識請求和回復(fù)。 【前五篇】系列文章傳送門: 網(wǎng)絡(luò)協(xié)議 14 - 流媒體協(xié)議:要說愛你不容易 網(wǎng)絡(luò)協(xié)議 15 - P2P 協(xié)議:小種子大學(xué)問 網(wǎng)絡(luò)協(xié)議 16 - DNS 協(xié)議:網(wǎng)絡(luò)世界的地址簿 網(wǎng)絡(luò)協(xié)議 17 - HTTPDNS:私人定制...
摘要:傳輸協(xié)議問題我們先解決第一個,傳輸協(xié)議的問題。信封里面的信分抬頭和正文板栗燜雞協(xié)議我們學(xué)過,這個請求使用方法,發(fā)送一個格式為的正文給,從而下一個單,這個訂單封裝在的信封里面,并且表明這是一筆交易,而且訂單的詳情都已經(jīng)寫明了。 【前五篇】系列文章傳送門: 網(wǎng)絡(luò)協(xié)議 15 - P2P 協(xié)議:小種子大學(xué)問 網(wǎng)絡(luò)協(xié)議 16 - DNS 協(xié)議:網(wǎng)絡(luò)世界的地址簿 網(wǎng)絡(luò)協(xié)議 17 - HTTPDN...
閱讀 3159·2021-11-22 09:34
閱讀 657·2021-11-22 09:34
閱讀 2516·2021-10-08 10:18
閱讀 3445·2021-09-22 15:57
閱讀 2698·2021-09-22 15:25
閱讀 2503·2019-08-30 15:54
閱讀 2255·2019-08-30 15:44
閱讀 1854·2019-08-29 11:18