【遠程調用框架】如何實現一個簡單的RPC框架(一)想法與設計

前言

最近在讀《大型網站系統與Java中間件實踐》這本書,第一次系統的簡單接觸分佈式的東西,瞭解到從單機應用到分佈式集羣大型應用的道路上,應用的拆分與服務化對於系統的維護、各模塊的解藕等都很是重要。這本書的第四章詳細介紹了服務框架的各個部分的實現原理。他分別從客戶端(服務調用者)以及服務端(服務發佈者)的角度介紹了服務框架基本的工做原理,又在基本工做原理的基礎上,介紹了服務治理等更加生產實際化的部分。
  研究生學習期間,接觸過一些rpc遠程調用框架的知識,例如webservice等等,關於webservice的基本知識、發佈調用等等進行過簡單基本的學習與實驗。16年暑假在淘寶實習,接觸了阿里很是重要的一個遠程調用框架的中間件——HSF,當時簡單的看了下源碼,明白了HSF基本的工做原理(其實就是全部遠程調用框架的基礎原理),又在實習轉正答辯的時候,瞭解了下webservice與hsf的不一樣。但有一點能夠確定的是,他們都是遠程調用服務框架,基礎的實現原理都是一致的。所以我想對這個通常rpc框架都會實現的原理親身實現一波,因而便有了這篇博客。整個系列目前分爲三篇博客,分別是想法與設計、實現、提高。
 其實很慚愧的是我既沒有看過webservice的源碼、也沒有深刻仔細推敲過HSF的源碼,所以準備在完成這個基礎的不能再基礎的RPC服務框架以後,認真分析一下HSF的源碼,也爲今年入職淘寶作準備。
 本人實現的這個「基礎的不能再基礎」的RPC服務框架就暫且命名爲LCRPC吧。
  • 1
  • 2
  • 3
  • 4
  • 5

一、什麼是RPC

RPC(Remote Procedure Call Protocol)——遠程過程調用協議,它是一種經過網絡從遠程計算機程序上請求服務,而不須要了解底層網絡技術的協議。RPC協議假定某些傳輸協議的存在,如TCP或UDP,爲通訊程序之間攜帶信息數據。在OSI網絡通訊模型中,RPC跨越了傳輸層和應用層。RPC使得開發包括網絡分佈式多程序在內的應用程序更加容易。 
RPC採用客戶機/服務器模式。請求程序就是一個客戶機,而服務提供程序就是一個服務器。首先,客戶機調用進程發送一個有進程參數的調用信息到服務進程,而後等待應答信息。在服務器端,進程保持睡眠狀態直到調用信息到達爲止。當一個調用信息到達,服務器得到進程參數,計算結果,發送答覆信息,而後等待下一個調用信息,最後,客戶端調用進程接收答覆信息,得到進程結果,而後調用執行繼續進行。 
-------《百度百科》html

二、webservice

Web service是一個平臺獨立的,低耦合的,自包含的、基於可編程的web的應用程序,可以使用開放的XML(標準通用標記語言下的一個子集)標準來描述、發佈、發現、協調和配置這些應用程序,用於開發分佈式的互操做的應用程序。[1] 
Web Service技術, 能使得運行在不一樣機器上的不一樣應用無須藉助附加的、專門的第三方軟件或硬件, 就可相互交換數據或集成。依據Web Service規範實施的應用之間, 不管它們所使用的語言、 平臺或內部協議是什麼, 均可以相互交換數據。Web Service是自描述、 自包含的可用網絡模塊, 能夠執行具體的業務功能。Web Service也很容易部署, 由於它們基於一些常規的產業標準以及已有的一些技術,諸如標準通用標記語言下的子集XML、HTTP。Web Service減小了應用接口的花費。Web Service爲整個企業甚至多個組織之間的業務流程的集成提供了一個通用機制。 
-------《百度百科》web

能夠參考筆者總結的webservice的相關博客: 
【webservice】關於WSDL 
【webservice】Java 發佈webservice 步驟 
【webservice】如何遠程調用Websevice服務 
【webservice】Java調用WebServicespring

三、阿里中間件HSF

https://wenku.baidu.com/view/1002761a6bd97f192279e908.html 能夠參考這篇文章編程

四、如何實現一個簡單的RPC框架

4.1 LCRPC服務框架包含的內容

LCRPC服務框架主要包含兩個部分,一個是提供給用戶使用的jar包,用於服務調用以及發佈。另外一個是服務註冊查找中心,不與用戶直接打交道,用於服務信息的註冊與查找。緩存

  • 一、一個應用依賴包 
    服務框架的核心部分,是服務框架開發的主要產物--應用依賴jar包。爲服務調用者以及服務提供者提供:註冊服務、發佈服務、調用服務等功能。完成服務框架的核心功能。

注意: 
- (1)一個服務對應一個接口,接口中包含多個方法 
- (2)服務由接口的全限定名+版本號做爲惟一標識tomcat

  • 二、服務註冊查找中心 
    服務框架經過服務註冊查找中心完成服務信息的管理。LCRPC在發佈服務的同時,將服務的信息(服務的惟一標識、地址等)註冊到服務註冊查找中心。這一,服務在調用時根據服務名稱進行地址等其餘信息的查找,使得調用端和服務提供端能夠經過地址來直接進行交互。服務註冊查找中心不與用戶直接交互。

4.2 客戶端與服務端工做原理

從服務調用者以及服務提供者的角度,分別說明該服務框架的基本工做原理。以下圖所示: 
這裏寫圖片描述服務器

(看不清能夠訪問:遠程調用框架工做原理) 
對於服務調用者來講: 
- (1)服務框架得到服務調用者提供的服務信息(服務惟一標識:接口全限定名+版本號;方法;調用參數); 
- (2)框架根據服務信息經過服務註冊查找中心查找到該服務提供者的地址列表; 
- (3)可根據(服務、接口、方法、參數)進行路由,肯定服務提供者的地址; 
- (4)拼裝請求參數對象Request,並序列化成二進制流; 
- (5)與服務端創建鏈接,發送序列化二進制結果; 
- (6)獲得服務端響應,反序列化,獲得最終調用結果網絡

對於服務提供者來講: 
- (1)發佈服務,監聽端口; 
- (2)服務發佈成功後,將服務信息(服務惟一標識:接口全限定名+版本號;服務實現類全限定名)註冊到服務註冊查找中心; 
- (3)接收客戶端請求,將請求數據反序列化爲Request對象; 
- (4)解析Request對象,根據服務標識從服務註冊查找中心獲取該服務信息,例如服務接口的實現類; 
- (5)利用反射建立類實例對象,調用方法(多采用線程池的方式); 
- (6)將調用結果序列化成二進制數據; 
-(7)發送響應數據到客戶端;數據結構

注意:服務發佈者須要提供給服務調用者一個二方包,包中函數接口全部方法調用的參數以及返回類型類。其實這個二方包最大的用處應該體如今:讓用戶像本地調用同樣使用服務框架完成遠程調用,可是第一個版本咱們先不實現這個功能,後面能夠進行優化。mvc

4.3 設計

4.3.1 涉及到的Java編程知識

  • (1)序列化與反序列化:請求的信息須要拼裝成Request對象,而且序列化成二進制信息發送給服務發佈者;服務發佈者對於接收到的二進制信息,須要反序列化爲Request對象,解析該對象進行服務的調用,而且將調用的結果也要序列化爲二進制對象返回給服務調用者。所以,在整個服務框架中,序列化/反序列化的做用在於調用端與發佈端數據的發送。
  • (2)反射:服務發佈端收到調用端的請求後,根據服務惟一標識能夠得到該接口的實現類信息,經過反射建立實現類實例,進行方法的調用;
  • (3)路由:因爲在分佈式集羣環境中,同一個服務的發佈者可能有不少,也就意味着從服務註冊查找中心查找到的服務地址可能多於一個,咱們能夠根據接口/方法/參數的粒度進行路由,同時還有一些其餘規則,例如同機房原則、歸組規則等等。在LCRPC第一個版本的實現中,咱們先採起最簡單的方式得到服務的地址,例如隨機選擇等方式;
  • (4)網絡通訊實現選擇:可參考博客《Java BIO NIO AIO 總結》。在LCRPC服務框架版本一中,咱們先採用BIO的方式,即採用Java Socket編程的方式,對每個鏈接創建一個新的線程維護;第二個版本使用Java NIO的通訊方式;然後能夠再用netty等現成的框架實現AIO。

4.3.2 設計

(一)服務註冊查找中心 
服務註冊查找中心最終應是一個web應用,提供服務接口給LCRPC服務框架使用,負責服務信息的管理,例如查找與註冊。 
- 一、使用開發框架:springmvc+maven+tomcat 
- 二、接口設計: 
(1)服務註冊接口:對服務信息進行存儲,服務對應接口全限定名+版本號做爲服務的惟一標識; 
(2)服務地址列表查詢接口:根據服務惟一標識,返回服務地址列表; 
(3)服務信息查詢接口:根據服務惟一標識,返回該服務全部信息(包括地址列表) 
- 三、數據結構設計 
(1)服務信息描述結構:ServiceInfoDO 
(2)註冊服務列表單例類:ServicesSingle 
(3)服務操做接口:IServiceAccess 
(4)服務操做接口實現類:ServiceAccessImpl 
(5)servlet:ServiceCenterServer 
類結構示意圖:(後面補充)

(二)LCRPC包 
提供給服務調用者和服務發佈者使用,應提供的功能包括:服務發佈(包含服務註冊)、服務調用等; 
- 一、使用開發框架:maven 
- 二、數據結構設計: 
(0)服務信息結構體:ServiceInfoDO 
(1)請求調用參數結構:LCRPCRequestDO 
(2)服務調用對外接口:ILCRPCConsumer LCRPCConsumerImpl 對外提供服務調用功能 
(3)服務發佈對外接口:ILCRPCProvider LCRPCProviderImpl 對外提供服務發佈功能(包含服務註冊) 
(4)服務調用幫助類接口:IConsumerService ConsumerServiceImpl 
(5)服務發佈幫助類接口:IProviderService ProviderServiceImpl 
(7)線程類一:socket服務端監聽線程 
(8)線程類二:socket服務端接收到鏈接後的處理線程 
(9)自定義異常類:LCRPCRemoteCallException、LCRPCServiceIDIsIllegal、LCRPCServiceMethodIsIllegal、LCRPCServiceNotFound、LCRPCServiceInfoNotComplete、LCRPCServiceListenFailed、LCRPCServiceRegistryFailed 
(10)常量類:Constant 
類結構示意圖:(後面補充)

(三)測試應用 
利用LCRPC服務框架提供的包,進行服務發佈和服務調用的測試。注意服務發佈者須要提供給服務調用者一個調用二方包,包中至少含有該服務多個方法的參數以及返回類型對應的類信息。

  • 一、服務發佈測試 
    這裏咱們設計一個計算器的服務,提供加減乘除四個功能方法,同時採用本身設計的方法參數以及方法返回值類型。
  • (1)使用開發框架:spring+maven
  • (2)數據結構 
    a、方法參數:MyParamDO 
    b、方法返回值:MyResultDO 
    c、服務對應接口:ICaculator 
    d、服務對應接口的實現類:CaculatorImpl 
    e、服務發佈測試類(主運行類):ProviderTest 
    f、常量類:Constant 
    最終服務發佈方須要生成一個二方包給服務調用者使用。

  • 二、服務調用測試 
    這裏咱們利用計算器服務提供的二方包,對該服務進行遠程調用

  • (1)使用開發框架:spring+maven
  • (2)數據結構 
    a、服務發佈者提供的二方包 
    b、測試類(主運行類):ConsumerTest

4.3.3 有可能存在的問題

  • (1)鏈接愈來愈多:IO模式的改變 
    在LCRPC第一個版本的實現中,咱們採用的是Java中BIO的通訊模式,即對每個鏈接都採用一個線程進行維護,隨着請求的增多,這會形成大量線程的建立維護以及資源的浪費。這一點可參考博客《Java BIO NIO AIO 總結》。所以爲了解決這個問題,咱們能夠嘗試改變通訊方式,例如採用BIO的模式,以及AIO的模式。

  • (2)如何讓用戶像本地調用同樣使用服務框架:面向接口編程+動態代理 
    在第一個版本中,服務調用者可以調用服務的必要基礎是:LCRPC服務框架提供的依賴、服務發佈者提供的二方包、以及要調用服務的相關信息(該服務的惟一標識、該服務方法名稱等等)。而其實咱們是否能夠幫助用戶像在本地使用方法同樣的去調用遠程服務。用戶只須要上述必要基礎的前兩樣,利用服務發佈者提供的二方包,該二方包中應當有服務對應的接口,用戶直接在本地調用該接口方法,就完成遠程調用的內容,沒必要在依靠(該服務的惟一標識、該服務方法名稱)等信息進行不那麼形象化的調用。而上述功能,能夠採用動態代理的方式實現。

  • (3)服務註冊查找中心:服務信息的持久化  在服務框架第一個版本的實現中,服務註冊查找中心對於全部已經註冊的信息緩存在內存中,這在實際生產中會產生的問題是:若是服務註冊查找服務須要重啓,則會丟失所有已經註冊服務的信息,所以須要考慮服務信息的持久化。

相關文章
相關標籤/搜索