
一個陽光明媚的早晨老婆又在翻看我訂閱的技術雜志?!袄瞎裁词荝PC呀為什么你們程序員那么多黑話”老婆還是一如既往的好奇?!癛PC就是Remote Procedure Call的簡稱呀翻譯成中文就是遠程過程調用嘛”我一邊看著書一邊漫不經心的回答著?!吧赌阍谡f啥誰不知道翻譯成中文是什么意思你個廢柴快給我滾去洗碗”“我去。。?!蔽胰鐗舫跣盐覍γ孀目刹皇且粋€程序員為了不去洗碗我瞬間調動起全部腦細胞星辰大海在我腦中匯聚靈感涌現......是這樣遠程過程調用自然是相對于本地過程調用來說的嘛?!班藕吣窍冉o老娘講講本地過程調用是啥子”“本地過程調用就好比你現在在家里你要想洗碗那你直接把碗放進洗碗機打開洗碗機開關就可以洗了。這就叫本地過程調用?!薄鞍ミ衔铱刹桓赡巧妒沁h程過程調用”“遠程嘛那就是你現在不在家跟姐妹們浪去了突然發(fā)現碗還沒洗打了個電話過來叫我去洗碗這就是遠程過程調用啦”多么通俗易懂的解釋我真是天才“哦我明白了”說著老婆開始收拾包包?!澳氵@是干啥去哦”“我我要出門浪去呀待會記得接收我的遠程調用哦哦不咱們要專業(yè)點應該說待會記得接收我的RPC哦”......溫馨提示非程序員請就此止步程序員請繼續(xù)往下閱讀......一、RPC 的科學解釋說起RPC就不能不提到分布式這個促使RPC誕生的領域。1.1 從本地調用到遠程調用假設你有一個計算器接口Calculator以及它的實現類CalculatorImpl那么在系統(tǒng)還是單體應用時你要調用Calculator的add方法來執(zhí)行一個加運算直接new一個CalculatorImpl然后調用add方法就行了這其實就是非常普通的本地函數調用因為在同一個地址空間或者說在同一塊內存所以通過方法棧和參數棧就可以實現。現在基于高性能和高可靠等因素的考慮你決定將系統(tǒng)改造為分布式應用將很多可以共享的功能都單獨拎出來比如上面說到的計算器你單獨把它放到一個服務里頭讓別的服務去調用它。這下問題來了服務A里頭并沒有CalculatorImpl這個類那它要怎樣調用服務B的CalculatorImpl的add方法呢1.2 RPC 的核心思想有同學會說可以模仿B/S架構的調用方式呀在B服務暴露一個Restful接口然后A服務通過調用這個Restful接口來間接調用CalculatorImpl的add方法。很好這已經很接近RPC了不過如果是這樣那每次調用時是不是都需要寫一串發(fā)起http請求的代碼呢比如httpClient.sendRequest...之類的能不能像本地調用一樣去發(fā)起遠程調用讓使用者感知不到遠程調用的過程呢像這樣Reference private Calculator calculator; ... calculator.add(1,2); ...這時候有同學就會說用代理模式呀而且最好是結合Spring IoC一起使用通過Spring注入calculator對象注入時如果掃描到對象加了Reference注解那么就給它生成一個代理對象將這個代理對象放進容器中。而這個代理對象的內部就是通過httpClient來實現RPC遠程過程調用的??赡苌厦孢@段描述比較抽象不過這就是很多RPC框架要解決的問題和解決的思路比如阿里的Dubbo。1.3 RPC 要解決的兩個核心問題解決分布式系統(tǒng)中服務之間的調用問題。遠程調用時要能夠像本地調用一樣方便讓調用者感知不到遠程調用的邏輯。二、RPC 的實現原理2.1 傳輸協(xié)議的選擇實際情況下RPC很少用到http協(xié)議來進行數據傳輸畢竟我只是想傳輸一下數據而已何必動用到一個文本傳輸的應用層協(xié)議呢我為什么不直接使用二進制傳輸比如直接用Java的Socket協(xié)議進行傳輸2.2 RPC 的完整過程不管你用何種協(xié)議進行數據傳輸一個完整的RPC過程都可以用下面這張圖來描述以左邊的Client端為例Application就是rpc的調用方Client Stub就是我們上面說到的代理對象也就是那個看起來像是Calculator的實現類其實內部是通過rpc方式來進行遠程調用的代理對象至于Client Run-time Library則是實現遠程調用的工具包比如jdk的Socket最后通過底層網絡實現實現數據的傳輸。2.3 序列化與反序列化這個過程中最重要的就是序列化和反序列化了因為數據傳輸的數據包必須是二進制的你直接丟一個Java對象過去人家可不認識你必須把Java對象序列化為二進制格式傳給Server端Server端接收到之后再反序列化為Java對象。下一次我也將通過代碼給大家演示一下如何實現一個簡單的RPC。三、RPC 與其他技術的對比3.1 RPC vs Restful其實這兩者并不是一個維度的概念總得來說RPC涉及的維度更廣。如果硬要比較那么可以從RPC風格的url和Restful風格的url上進行比較。比如你提供一個查詢訂單的接口用RPC風格你可能會這樣寫/queryOrder?orderId123用Restful風格呢Get /order?orderId123RPC是面向過程Restful是面向資源并且使用了Http動詞。從這個維度上看Restful風格的url在表述的精簡性、可讀性上都要更好。3.2 RPC vs RMI嚴格來說這兩者也不是一個維度的。RMI是Java提供的一種訪問遠程對象的協(xié)議是已經實現好了的可以直接用了。而RPC呢人家只是一種編程模型并沒有規(guī)定你具體要怎樣實現你甚至都可以在你的RPC框架里面使用RMI來實現數據的傳輸比如DubboDubbo - rmi協(xié)議四、RPC 框架的復雜性要實現一個RPC不算難難的是實現一個高性能高可靠的RPC框架。4.1 服務發(fā)現與負載均衡比如既然是分布式了那么一個服務可能有多個實例你在調用時要如何獲取這些實例的地址呢這時候就需要一個服務注冊中心比如在Dubbo里頭就可以使用Zookeeper作為注冊中心在調用時從Zookeeper獲取服務的實例列表再從中選擇一個進行調用。那么選哪個調用好呢這時候就需要負載均衡了于是你又得考慮如何實現復雜均衡比如Dubbo就提供了好幾種負載均衡策略。4.2 性能優(yōu)化與緩存這還沒完總不能每次調用時都去注冊中心查詢實例列表吧這樣效率多低呀于是又有了緩存有了緩存就要考慮緩存的更新問題blablabla......4.3 其他高級特性你以為就這樣結束了沒呢還有這些客戶端總不能每次調用完都干等著服務端返回數據吧于是就要支持異步調用服務端的接口修改了老的接口還有人在用怎么辦總不能讓他們都改了吧這就需要版本控制了服務端總不能每次接到請求都馬上啟動一個線程去處理吧于是就需要線程池服務端關閉時還沒處理完的請求怎么辦是直接結束呢還是等全部請求處理完再關閉呢......如此種種都是一個優(yōu)秀的RPC框架需要考慮的問題。當然接下來我們還是先實現一個簡單的RPC再在上面一步步優(yōu)化傳送門 如何實現一個簡單的RPC五、總結RPC作為分布式系統(tǒng)的核心技術之一其核心價值在于讓遠程服務調用像本地調用一樣簡單透明。從生活化的比喻到技術實現我們看到了RPC如何通過代理模式、序列化、網絡傳輸等機制將復雜的遠程通信細節(jié)封裝起來為開發(fā)者提供簡潔的編程接口。在實際應用中一個成熟的RPC框架還需要考慮服務發(fā)現、負載均衡、容錯處理、性能優(yōu)化等諸多方面。理解RPC的基本原理有助于我們更好地使用和設計分布式系統(tǒng)。六、參考資料wikipedia - RPCwhat-is-restful-rest-vs-rpcdifference-between-rpc-and-rmi.htmlwhat-is-the-difference-between-java-rmi-and-rpcDubbo 使用文檔Dubbo 源碼開發(fā)手冊一本很棒的分布式書籍《大型網站系統(tǒng)與Java中間件實踐》