RPC(Remote Procedure Call Protocol)——遠程過程調用協議,它是一種經過網絡從遠程計算機程序上請求服務,而不須要了解底層網絡技術的協議。RPC協議假定某些傳輸協議的存在,如TCP或UDP,爲通訊程序之間攜帶信息數據。在OSI網絡通訊模型中,RPC跨越了傳輸層和應用層。RPC使得開發包括網絡分佈式多程序在內的應用程序更加容易。java
RPC採用客戶機/服務器模式。請求程序就是一個客戶機,而服務提供程序就是一個服務器。首先,客戶機調用進程發送一個有進程參數的調用信息到服務進程,而後等待應答信息。在服務器端,進程保持睡眠狀態直到調用信息到達爲止。當一個調用信息到達,服務器得到進程參數,計算結果,發送答覆信息,而後等待下一個調用信息,最後,客戶端調用進程接收答覆信息,得到進程結果,而後調用執行繼續進行。web
其實這是應用開發到必定的階段的強烈需求驅動的。編程
1. 若是咱們開發簡單的單一應用,邏輯簡單、用戶很少、流量不大,那咱們用不着;json
2. 當咱們的系統訪問量增大、業務增多時,咱們會發現一臺單機運行此係統已經沒法承受。此時,咱們能夠將業務拆分紅幾個互不關聯的應用,分別部署在各自機器上,以劃清邏輯並減少壓力。此時,咱們也能夠不須要RPC,由於應用之間是互不關聯的。
3. 當咱們的業務愈來愈多、應用也愈來愈多時,天然的,咱們會發現有些功能已經不能簡單劃分開來或者劃分不出來。此時,能夠將公共業務邏輯抽離出來,將之組成獨立的服務Service應用 。而原有的、新增的應用均可以與那些獨立的Service應用 交互,以此來完成完整的業務功能。因此此時,咱們急需一種高效的應用程序之間的通信手段來完成這種需求,因此你看,RPC大顯身手的時候來了!
其實3描述的場景也是服務化 、微服務 和分佈式系統架構 的基礎場景。即RPC框架就是實現以上結構的有力方式。服務器
Nelson 的論文中指出實現 RPC 的程序包括 5 個部分:網絡
1. User數據結構
2. User-stub架構
3. RPCRuntime併發
4. Server-stub負載均衡
5. Server
這 5 個部分的關係以下圖所示
目前經常使用的RPC框架以下:
1. Thrift:thrift是一個軟件框架,用來進行可擴展且跨語言的服務的開發。它結合了功能強大的軟件堆棧和代碼生成引擎,以構建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 這些編程語言間無縫結合的、高效的服務。
2. Dubbo:Dubbo是一個分佈式服務框架,以及SOA治理方案。其功能主要包括:高性能NIO通信及多協議集成,服務動態尋址與路由,軟負載均衡與容錯,依賴分析與降級等。 Dubbo是阿里巴巴內部的SOA服務化治理方案的核心框架,Dubbo自2011年開源後,已被許多非阿里系公司使用。
3. Spring Cloud:Spring Cloud由衆多子項目組成,如Spring Cloud Config、Spring Cloud Netflix、Spring Cloud Consul 等,提供了搭建分佈式系統及微服務經常使用的工具,如配置管理、服務發現、斷路器、智能路由、微代理、控制總線、一次性token、全局鎖、選主、分佈式會話和集羣狀態等,知足了構建微服務所需的全部解決方案。Spring Cloud基於Spring Boot, 使得開發部署極其簡單。
1. 功能差別
在架構上,RPC和Message的差別點是,Message有一箇中間結點Message Queue,能夠把消息存儲。
消息的特色
1. Message Queue把請求的壓力保存一下,逐漸釋放出來,讓處理者按照本身的節奏來處理。
2. Message Queue引入一下新的結點,系統的可靠性會受Message Queue結點的影響。
3. Message Queue是異步單向的消息。發送消息設計成是不須要等待消息處理的完成。
因此對於有同步返回需求,用Message Queue則變得麻煩了。
RPC的特色
同步調用,對於要等待返回結果/處理結果的場景,RPC是能夠很是天然直覺的使用方式(RPC也能夠是異步調用)。
因爲等待結果,Consumer(Client)會有線程消耗。若是以異步RPC的方式使用,Consumer(Client)線程消耗能夠去掉。但不能作到像消息同樣暫存消息/請求,壓力會直接傳導到服務Provider。
2. 適用場合差別
1. 但願同步獲得結果的場合,RPC合適。
2. 但願使用簡單,則RPC;RPC操做基於接口,使用簡單,使用方式模擬本地調用。異步的方式編程比較複雜。
3. 不但願發送端(RPC Consumer、Message Sender)受限於處理端(RPC Provider、Message Receiver)的速度時,使用Message Queue。
隨着業務增加,有的處理端處理量會成爲瓶頸,會進行同步調用到異步消息的改造。這樣的改造實際上有調整業務的使用方式。好比原來一個操做頁面提交後就下一個頁面會看處處理結果;改造後異步消息後,下一個頁面就會變成「操做已提交,完成後會獲得通知」。
3. 不適用場合說明
1. RPC同步調用使用Message Queue來傳輸調用信息。 上面分析能夠知道,這樣的作法,發送端是在等待,同時佔用一箇中間點的資源。變得複雜了,但沒有對等的收益。
2. 對於返回值是void的調用,能夠這樣作,由於實際上這個調用業務上每每不須要同步獲得處理結果的,只要保證會處理便可。(RPC的方式能夠保證調用返回即處理完成,使用消息方式後這一點不能保證了。)
3. 返回值是void的調用,使用消息,效果上是把消息的使用方式Wrap成了服務調用(服務調用使用方式成簡單,基於業務接口)。
RPC框架實現的幾個核心技術點:
(1)服務暴露:
遠程提供者須要以某種形式提供服務調用相關的信息,包括但不限於服務接口定義、數據結構、或者中間態的服務定義文件。例如Facebook的Thrift的IDL文件,Web service的WSDL文件;服務的調用者須要經過必定的途徑獲取遠程服務調用相關的信息。
目前,大部分跨語言平臺 RPC 框架採用根據 IDL 定義經過 code generator 去生成 stub 代碼,這種方式下實際導入的過程就是經過代碼生成器在編譯期完成的。代碼生成的方式對跨語言平臺 RPC 框架而言是必然的選擇,而對於同一語言平臺的 RPC 則能夠經過共享接口定義來實現。這裏的導入方式本質也是一種代碼生成技術,只不過是在運行時生成,比靜態編譯期的代碼生成看起來更簡潔些。
java 中還有一種比較特殊的調用就是多態,也就是一個接口可能有多個實現,那麼遠程調用時到底調用哪一個?這個本地調用的語義是經過 jvm 提供的引用多態性隱式實現的,那麼對於 RPC 來講跨進程的調用就無法隱式實現了。若是前面DemoService 接口有 2 個實現,那麼在導出接口時就須要特殊標記不一樣的實現須要,那麼遠程調用時也須要傳遞該標記才能調用到正確的實現類,這樣就解決了多態調用的語義問題。
(2)遠程代理對象:
服務調用者用的服務實際是遠程服務的本地代理。說白了就是經過動態代理來實現。
java 裏至少提供了兩種技術來提供動態代碼生成,一種是 jdk 動態代理,另一種是字節碼生成。動態代理相比字節碼生成使用起來更方便,但動態代理方式在性能上是要遜色於直接的字節碼生成的,而字節碼生成在代碼可讀性上要差不少。二者權衡起來,我的認爲犧牲一些性能來得到代碼可讀性和可維護性顯得更重要。
(3)通訊:
RPC框架與具體的協議無關。RPC 可基於 HTTP 或 TCP 協議,Web Service 就是基於 HTTP 協議的 RPC,它具備良好的跨平臺性,但其性能卻不如基於 TCP 協議的 RPC。
1. TCP/HTTP:衆所周知,TCP 是傳輸層協議,HTTP 是應用層協議,而傳輸層較應用層更加底層,在數據傳輸方面,越底層越快,所以,在通常狀況下,TCP 必定比 HTTP 快。
2. 消息ID:RPC 的應用場景實質是一種可靠的請求應答消息流,和 HTTP 相似。所以選擇長鏈接方式的 TCP 協議會更高效,與 HTTP 不一樣的是在協議層面咱們定義了每一個消息的惟一 id,所以能夠更容易的複用鏈接。
3. IO方式:爲了支持高併發,傳統的阻塞式 IO 顯然不太合適,所以咱們須要異步的 IO,即 NIO。Java 提供了 NIO 的解決方案,Java 7 也提供了更優秀的 NIO.2 支持。
4. 多鏈接:既然使用長鏈接,那麼第一個問題是到底 client 和 server 之間須要多少根鏈接?實際上單鏈接和多鏈接在使用上沒有區別,對於數據傳輸量較小的應用類型,單鏈接基本足夠。單鏈接和多鏈接最大的區別在於,每根鏈接都有本身私有的發送和接收緩衝區,所以大數據量傳輸時分散在不一樣的鏈接緩衝區會獲得更好的吞吐效率。因此,若是你的數據傳輸量不足以讓單鏈接的緩衝區一直處於飽和狀態的話,那麼使用多鏈接並不會產生任何明顯的提高,反而會增長鏈接管理的開銷。
5. 心跳:鏈接是由 client 端發起創建並維持。若是 client 和 server 之間是直連的,那麼鏈接通常不會中斷(固然物理鏈路故障除外)。若是 client 和 server 鏈接通過一些負載中轉設備,有可能鏈接一段時間不活躍時會被這些中間設備中斷。爲了保持鏈接有必要定時爲每一個鏈接發送心跳數據以維持鏈接不中斷。心跳消息是 RPC 框架庫使用的內部消息,在前文協議頭結構中也有一個專門的心跳位,就是用來標記心跳消息的,它對業務應用透明。
(4)序列化:
兩方面會直接影響 RPC 的性能,一是傳輸方式,二是序列化。
1. 序列化方式:畢竟是遠程通訊,須要將對象轉化成二進制流進行傳輸。不一樣的RPC框架應用的場景不一樣,在序列化上也會採起不一樣的技術。 就序列化而言,Java 提供了默認的序列化方式,但在高併發的狀況下,這種方式將會帶來一些性能上的瓶頸,因而市面上出現了一系列優秀的序列化框架,好比:Protobuf、Kryo、Hessian、Jackson 等,它們能夠取代 Java 默認的序列化,從而提供更高效的性能。
2. 編碼內容:出於效率考慮,編碼的信息越少越好(傳輸數據少),編碼的規則越簡單越好(執行效率高)。以下是編碼須要具有的信息:
除了以上這些必須的調用信息,咱們可能還須要一些元信息以方便程序編解碼以及將來可能的擴展。這樣咱們的編碼消息裏面就分紅了兩部分,一部分是元信息、另外一部分是調用的必要信息。若是設計一種 RPC 協議消息的話,元信息咱們把它放在協議消息頭中,而必要信息放在協議消息體中。下面給出一種概念上的 RPC 協議消息設計格式:
以上就是RPC的核心技術點,包含內容繁多,下面本博就在學習的基礎上以三種RPC框架爲例介紹RPC的各項關鍵技術。