看了這篇Dubbo RPC面試題,讓天下沒有難面的面試題!

Java工程師攻略發表於2019-05-08

前言:

RPC非常重要,很多人面試的時候都掛在了這個地方!你要是還不懂RPC是什麼?他的基本原理是什麼?你一定要把下邊的內容記起來!好好研究一下!特別是文中給出的一張關於RPC的基本流程圖,重點中的重點,Dubbo RPC的基本執行流程就是他,RPC框架的基本原理也是他,別說我沒告訴你!看了下邊的內容你要掌握的內容如下,當然還有很多:

  • RPC的由來,是怎樣一步步演進出來的;
  • RPC的基本架構是什麼;
  • RPC的基本實現原理,就是下邊的這張圖,重點中的重點;
  • REST 和 SOAP、RPC 有何區別呢?
  • 整個呼叫的過程經歷了哪幾步和Spring MVC的執行流程一樣,相當重要;

一、為什麼要有RPC

隨著網際網路的發展,網站應用的規模不斷擴大,常規的垂直應用架構已無法應對,分散式服務架構以及流動計算架構勢在必行,亟需一個治理系統確保架構有條不紊的演進。

image

1、單一應用架構

當網站流量很小時,只需一個應用,將所有功能都部署在一起,以減少部署節點和成本。此時,用於簡化增刪改查工作量的資料訪問框架(ORM) 是關鍵。

2、垂直應用架構

當訪問量逐漸增大,單一應用增加機器帶來的加速度越來越小,將應用拆成互不相干的幾個應用,以提升效率。此時,用於加速前端頁面開發的 Web框架(MVC) 是關鍵。

3、分散式服務架構

當垂直應用越來越多,應用之間互動不可避免,將核心業務抽取出來,作為獨立的服務,逐漸形成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。

此時,用於提高業務複用及整合的分散式服務框架(RPC),提供統一的服務是關鍵。

例如:各個團隊的服務提供方就不要各自實現一套序列化、反序列化、網路框架、連線池、收發執行緒、超時處理、狀態機等“業務之外”的重複技術勞動,造成整體的低效。

PS: 其實上述三個原因也是為什麼要有Dubbo的原因!不信你去Dubbo官網去看!

4、流動計算架構

PS:這個屬於擴充套件內容,摘自Dubbo官網,屬於架構演進的一個過程

當服務越來越多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增加一個排程中心基於訪問壓力實時管理叢集容量,提高叢集利用率。此時,用於提高機器利用率的資源排程和治理中心(SOA)是關鍵。

5、另外一個原因

就是因為在幾個程式內(應用分佈在不同的機器上),無法共用記憶體空間,或者在一臺機器內通過本地呼叫無法完成相關的需求,比如不同的系統之間的通訊,甚至不同組織之間的通訊。此外由於機器的橫向擴充套件,需要在多臺機器組成的叢集上部署應用等等。

所以,統一RPC框架來解決提供統一的服務。

二、什麼是RPC

RPC(Remote Procedure Call Protocol)遠端過程呼叫協議,它是一種通過網路從遠端計算機程式上請求服務,而不需要了解底層網路技術的協議。簡言之,RPC使得程式能夠像訪問本地系統資源一樣,去訪問遠端系統資源。比較關鍵的一些方面包括:通訊協議、序列化、資源(介面)描述、服務框架、效能、語言支援等。

image

簡單的說,RPC就是從一臺機器(客戶端)上通過引數傳遞的方式呼叫另一臺機器(伺服器)上的一個函式或方法(可以統稱為服務)並得到返回的結果。

三、PRC架構元件

一個基本的RPC架構裡面應該至少包含以下4個元件:

  1. 客戶端 (Client):服務呼叫方(服務消費者)
  2. 客戶端存根 (Client Stub):存放服務端地址資訊,將客戶端的請求引數資料資訊打包成網路訊息,再通過網路傳輸傳送給服務端
  3. 服務端存根 (Server Stub):接收客戶端傳送過來的請求訊息並進行解包,然後再呼叫本地服務進行處理
  4. 服務端 (Server):服務的真正提供者

image

具體呼叫過程:

  1. 服務消費者(client客戶端)通過呼叫本地服務的方式呼叫需要消費的服務;
  2. 客戶端存根(client stub)接收到呼叫請求後負責將方法、入參等資訊序列化(組裝)成能夠進行網路傳輸的訊息體;
  3. 客戶端存根(client stub)找到遠端的服務地址,並且將訊息通過網路傳送給服務端;
  4. 服務端存根(server stub)收到訊息後進行解碼(反序列化操作);
  5. 服務端存根(server stub)根據解碼結果呼叫本地的服務進行相關處理;
  6. 本地服務執行具體業務邏輯並將處理結果返回給服務端存根(server stub);
  7. 服務端存根(server stub)將返回結果重新打包成訊息(序列化)並通過網路傳送至消費方;
  8. 客戶端存根(client stub)接收到訊息,並進行解碼(反序列化);
  9. 服務消費方得到最終結果;

而RPC框架的實現目標則是將上面的第2-10步完好地封裝起來,也就是把呼叫、編碼/解碼的過程給封裝起來,讓使用者感覺上像呼叫本地服務一樣的呼叫遠端服務。

四、RPC和SOA、SOAP、REST的區別

1、REST

可以看著是HTTP協議的一種直接應用,預設基於JSON作為傳輸格式,使用簡單,學習成本低效率高,但是安全性較低。

2、SOAP

SOAP是一種資料交換協議規範,是一種輕量的、簡單的、基於XML的協議的規範。而SOAP可以看著是一個重量級的協議,基於XML、SOAP在安全方面是通過使用XML-Security和XML-Signature兩個規範組成了WS-Security來實現安全控制的,當前已經得到了各個廠商的支援 。

它有什麼優點?簡單總結為:易用、靈活、跨語言、跨平臺。

3、SOA

面向服務架構,它可以根據需求通過網路對鬆散耦合的粗粒度應用元件進行分散式部署、組合和使用。服務層是SOA的基礎,可以直接被應用呼叫,從而有效控制系統中與軟體代理互動的人為依賴性。

SOA是一種粗粒度、鬆耦合服務架構,服務之間通過簡單、精確定義介面進行通訊,不涉及底層程式設計介面和通訊模型。SOA可以看作是B/S模型、XML(標準通用標記語言的子集)/Web Service技術之後的自然延伸。

4、REST 和 SOAP、RPC 有何區別呢?

沒什麼太大區別,他們的本質都是提供可支援分散式的基礎服務,最大的區別在於他們各自的的特點所帶來的不同應用場景 。

五、RPC框架需要解決的問題?

  1. 如何確定客戶端和服務端之間的通訊協議?
  2. 如何更高效地進行網路通訊?
  3. 服務端提供的服務如何暴露給客戶端?
  4. 客戶端如何發現這些暴露的服務?
  5. 如何更高效地對請求物件和響應結果進行序列化和反序列化操作?

六、RPC的實現基礎?

  1. 需要有非常高效的網路通訊,比如一般選擇Netty作為網路通訊框架;
  2. 需要有比較高效的序列化框架,比如谷歌的Protobuf序列化框架;
  3. 可靠的定址方式(主要是提供服務的發現),比如可以使用Zookeeper來註冊服務等等;
  4. 如果是帶會話(狀態)的RPC呼叫,還需要有會話和狀態保持的功能;

七、RPC使用了哪些關鍵技術?

1、動態代理

生成Client Stub(客戶端存根)和Server Stub(服務端存根)的時候需要用到Java動態代理技術,可以使用JDK提供的原生的動態代理機制,也可以使用開源的:CGLib代理,Javassist位元組碼生成技術。

2、序列化和反序列化

在網路中,所有的資料都將會被轉化為位元組進行傳送,所以為了能夠使引數物件在網路中進行傳輸,需要對這些引數進行序列化和反序列化操作。

  • **序列化:**把物件轉換為位元組序列的過程稱為物件的序列化,也就是編碼的過程。
  • **反序列化:**把位元組序列恢復為物件的過程稱為物件的反序列化,也就是解碼的過程。

目前比較高效的開源序列化框架:如Kryo、FastJson和Protobuf等。

3、NIO通訊

出於併發效能的考慮,傳統的阻塞式 IO 顯然不太合適,因此我們需要非同步的 IO,即 NIO。Java 提供了 NIO 的解決方案,Java 7 也提供了更優秀的 NIO.2 支援。可以選擇Netty或者MINA來解決NIO資料傳輸的問題。

4、服務註冊中心

可選:Redis、Zookeeper、Consul 、Etcd。一般使用ZooKeeper提供服務註冊與發現功能,解決單點故障以及分散式部署的問題(註冊中心)。

八、主流RPC框架有哪些

  1. RMI
  2. Hessian
  3. protobuf-rpc-pro
  4. Thrift
  5. Avro
  6. Dubbo

九、RPC的實現原理架構圖

image

image

**PS:**這張圖非常重點,是PRC的基本原理,請大家一定記住!

也就是說兩臺伺服器A,B,一個應用部署在A伺服器上,想要呼叫B伺服器上應用提供的函式/方法,由於不在一個記憶體空間,不能直接呼叫,需要通過網路來表達呼叫的語義和傳達呼叫的資料。

比如說,A伺服器想呼叫B伺服器上的一個方法:

User getUserByName(String userName)

1、建立通訊

  • 首先要解決通訊的問題:即A機器想要呼叫B機器,首先得建立起通訊連線。
  • 主要是通過在客戶端和伺服器之間建立TCP連線,遠端過程呼叫的所有交換的資料都在這個連線裡傳輸。連線可以是按需連線,呼叫結束後就斷掉,也可以是長連線,多個遠端過程呼叫共享同一個連線。
  • 通常這個連線可以是按需連線(需要呼叫的時候就先建立連線,呼叫結束後就立馬斷掉),也可以是長連線(客戶端和伺服器建立起連線之後保持長期持有,不管此時有無資料包的傳送,可以配合心跳檢測機制定期檢測建立的連線是否存活有效),多個遠端過程呼叫共享同一個連線。

2、服務定址

  • 要解決定址的問題,也就是說,A伺服器上的應用怎麼告訴底層的RPC框架,如何連線到B伺服器(如主機或IP地址)以及特定的埠,方法的名稱名稱是什麼。
  • 通常情況下我們需要提供B機器(主機名或IP地址)以及特定的埠,然後指定呼叫的方法或者函式的名稱以及入參出參等資訊,這樣才能完成服務的一個呼叫。
  • 可靠的定址方式(主要是提供服務的發現)是RPC的實現基石,比如可以採用Redis或者Zookeeper來註冊服務等等。

image

2.1、從服務提供者的角度看:

當服務提供者啟動的時候,需要將自己提供的服務註冊到指定的註冊中心,以便服務消費者能夠通過服務註冊中心進行查詢;

當服務提供者由於各種原因致使提供的服務停止時,需要向註冊中心登出停止的服務;

服務的提供者需要定期向服務註冊中心傳送心跳檢測,服務註冊中心如果一段時間未收到來自服務提供者的心跳後,認為該服務提供者已經停止服務,則將該服務從註冊中心上去掉。

2.2、從呼叫者的角度看:

服務的呼叫者啟動的時候根據自己訂閱的服務向服務註冊中心查詢服務提供者的地址等資訊;

當服務呼叫者消費的服務上線或者下線的時候,註冊中心會告知該服務的呼叫者;

服務呼叫者下線的時候,則取消訂閱。

3、網路傳輸

3.1、序列化

當A機器上的應用發起一個RPC呼叫時,呼叫方法和其入參等資訊需要通過底層的網路協議如TCP傳輸到B機器,由於網路協議是基於二進位制的,所有我們傳輸的引數資料都需要先進行序列化(Serialize)或者編組(marshal)成二進位制的形式才能在網路中進行傳輸。然後通過定址操作和網路傳輸將序列化或者編組之後的二進位制資料傳送給B機器。

3.2、反序列化

當B機器接收到A機器的應用發來的請求之後,又需要對接收到的引數等資訊進行反序列化操作(序列化的逆操作),即將二進位制資訊恢復為記憶體中的表達方式,然後再找到對應的方法(定址的一部分)進行本地呼叫(一般是通過生成代理Proxy去呼叫,

通常會有JDK動態代理、CGLIB動態代理、Javassist生成位元組碼技術等),之後得到呼叫的返回值。

4、服務呼叫

B機器進行本地呼叫(通過代理Proxy和反射呼叫)之後得到了返回值,此時還需要再把返回值傳送回A機器,同樣也需要經過序列化操作,然後再經過網路傳輸將二進位制資料傳送回A機器,而當A機器接收到這些返回值之後,則再次進行反序列化操作,恢復為記憶體中的表達方式,最後再交給A機器上的應用進行相關處理(一般是業務邏輯處理操作)。

通常,經過以上四個步驟之後,一次完整的RPC呼叫算是完成了,另外可能因為網路抖動等原因需要重試等。

相關文章