如何實現RPC遠程服務調用


RPC 調用的過程

  服務消費者稱為客戶端,服務提供者稱為服務端,兩者通常位於網絡上兩個不同的地址,要完成一次 RPC 調用,就必須先建立網絡連接。建立連接后,雙方還必須按照某種約定的協議進行網絡通信,這個協議就是通信協議。雙方能夠正常通信后,服務端接收到請求時,需要以某種方式進行處理,處理成功后,把請求結果返回給客戶端。為了減少傳輸的數據大小,還要對數據進行壓縮,也就是對數據進行序列化。

  想要完成調用,你需要解決四個問題:客戶端和服務端如何建立網絡連接?服務端如何處理請求?數據傳輸采用什么協議?數據該如何序列化和反序列化?

 

客戶端和服務端如何建立網絡連接?

客戶端和服務端之間基於 TCP 協議建立網絡連接最常用的途徑有兩種。

1. HTTP 通信

HTTP 通信是基於應用層 HTTP 協議的,而 HTTP 協議又是基於傳輸層 TCP 協議的。

一次 HTTP 通信過程就是發起一次 HTTP 調用,而一次 HTTP 調用就會建立一個 TCP 連接,經歷“三次握手”來建立鏈接,完成請求后,又經過“四次揮手”來斷開連接。

2. Socket 通信

  Socket 通信是基於 TCP/IP 協議的封裝,建立一次 Socket 連接至少需要一對套接字,其中一個運行於客戶端,稱為 ClientSocket ;另一個運行於服務器端,稱為 ServerSocket 。就像下圖所描述的,Socket 通信的過程分為四個步驟:服務器監聽、客戶端請求、連接確認、數據傳輸。

  服務器監聽:ServerSocket 通過調用 bind() 函數綁定某個具體端口,然后調用 listen() 函數實時監控網絡狀態,等待客戶端的連接請求。

  客戶端請求:ClientSocket 調用 connect() 函數向 ServerSocket 綁定的地址和端口發起連接請求。

  服務端連接確認:當 ServerSocket 監聽到或者接收到 ClientSocket 的連接請求時,調用 accept() 函數響應 ClientSocket 的請求,同客戶端建立連接。

  數據傳輸:當 ClientSocket 和 ServerSocket 建立連接后,ClientSocket 調用 send() 函數,ServerSocket 調用 receive() 函數,ServerSocket 處理完請求后,調用 send() 函數,ClientSocket 調用 receive() 函數,就可以得到得到返回結果。

 

 

  當客戶端和服務端建立網絡連接后,就可以發起請求了。但網絡不一定總是可靠的,經常會遇到網絡閃斷、連接超時、服務端宕機等各種異常,通常的處理手段有兩種。

鏈路存活檢測:客戶端需要定時地發送心跳檢測消息(一般是通過 ping 請求)給服務端,如果服務端連續 n 次心跳檢測或者超過規定的時間都沒有回復消息,則認為此時鏈路已經失效,這個時候客戶端就需要重新與服務端建立連接。

斷連重試:通常有多種情況會導致連接斷開,比如客戶端主動關閉、服務端宕機或者網絡故障等。這個時候客戶端就需要與服務端重新建立連接,但一般不能立刻完成重連,而是要等待固定的間隔后再發起重連,避免服務端的連接回收不及時,而客戶端瞬間重連的請求太多而把服務端的連接數占滿。

 

服務端如何處理請求?

同步阻塞方式(BIO),客戶端每發一次請求,服務端就生成一個線程去處理。當客戶端同時發起的請求很多時,服務端需要創建很多的線程去處理每一個請求,如果達到了系統最大的線程數瓶頸,新來的請求就沒法處理了。

同步非阻塞方式 (NIO),客戶端每發一次請求,服務端並不是每次都創建一個新線程來處理,而是通過 I/O 多路復用技術進行處理。就是把多個 I/O 的阻塞復用到同一個 select 的阻塞上,從而使系統在單線程的情況下可以同時處理多個客戶端請求。這種方式的優勢是開銷小,不用為每個請求創建一個線程,可以節省系統開銷。

異步非阻塞方式(AIO),客戶端只需要發起一個 I/O 操作然后立即返回,等 I/O 操作真正完成以后,客戶端會得到 I/O 操作完成的通知,此時客戶端只需要對數據進行處理就好了,不需要進行實際的 I/O 讀寫操作,因為真正的 I/O 讀取或者寫入操作已經由內核完成了。這種方式的優勢是客戶端無需等待,不存在阻塞等待問題。

適用場景:

BIO 適用於連接數比較小的業務場景,這樣的話不至於系統中沒有可用線程去處理請求。這種方式寫的程序也比較簡單直觀,易於理解。

NIO 適用於連接數比較多並且請求消耗比較輕的業務場景,比如聊天服務器。這種方式相比 BIO,相對來說編程比較復雜。

AIO 適用於連接數比較多而且請求消耗比較重的業務場景,比如涉及 I/O 操作的相冊服務器。這種方式相比另外兩種,編程難度最大,程序也不易於理解。

 

數據傳輸采用什么協議?

最常用的有 HTTP 協議,它是一種開放的協議,各大網站的服務器和瀏覽器之間的數據傳輸大都采用了這種協議。還有一些定制的私有協議,比如阿里巴巴開源的 Dubbo 協議,也可以用於服務端和客戶端之間的數據傳輸。論是開放的還是私有的協議,都必須定義一個“契約”,以便服務消費者和服務提供者之間能夠達成共識。服務消費者按照契約,對傳輸的數據進行編碼,然后通過網絡傳輸過去;服務提供者從網絡上接收到數據后,按照契約,對傳輸的數據進行解碼,然后處理請求,再把處理后的結果進行編碼,通過網絡傳輸返回給服務消費者;服務消費者再對返回的結果進行解碼,最終得到服務提供者處理后的返回值。

 

數據該如何序列化和反序列化?

  一般數據在網絡中進行傳輸前,都要先在發送方一端對數據進行編碼,經過網絡傳輸到達另一端后,再對數據進行解碼,這個過程就是序列化和反序列化。

  序列化是解決內存數據到字節流的相互轉換的,其次可以壓縮數據,這也是評估一種序列化方式的優劣因素之一。

  常用的序列化方式分為兩類:文本類如 XML/JSON 等,二進制類如 PB/Thrift 等,而具體采用哪種序列化方式,主要取決於三個方面的因素。

   支持數據結構類型的豐富度。數據結構種類支持的越多越好,這樣的話對於使用者來說在編程時更加友好,有些序列化框架如 Hessian 2.0 還支持復雜的數據結構比如 Map、List 等。

  跨語言支持。序列化方式是否支持跨語言也是一個很重要的因素,否則使用的場景就比較局限,比如 Java 序列化只支持 Java 語言,就不能用於跨語言的服務調用了。

  性能。主要看兩點,一個是序列化后的壓縮比,一個是序列化的速度。以常用的 PB 序列化和 JSON 序列化協議為例來對比分析,PB 序列化的壓縮比和速度都要比 JSON 序列化高很多,所以對性能和存儲空間要求比較高的系統選用 PB 序列化更合適;而 JSON 序列化雖然性能要差一些,但可讀性更好,更適合對外部提供服務。

 

總結

通信框架。它主要解決客戶端和服務端如何建立連接、管理連接以及服務端如何處理請求的問題。

通信協議。它主要解決客戶端和服務端采用哪種數據傳輸協議的問題。

序列化和反序列化。它主要解決客戶端和服務端采用哪種數據編解碼的問題。

  這三個部分就組成了一個完整的 RPC 調用框架,通信框架提供了基礎的通信能力,通信協議描述了通信契約,而序列化和反序列化則用於數據的編 / 解碼。一個通信框架可以適配多種通信協議,也可以采用多種序列化和反序列化的格式,比如服務化框架 Dubbo 不僅支持 Dubbo 協議,還支持 RMI 協議、HTTP 協議等,而且還支持多種序列化和反序列化格式,比如 JSON、Hession 2.0 以及 Java 序列化等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM