RPC(Remote Procedure Calls)遠程過程調用

很長時間以來都沒有怎麼好好搞清楚RPC(即Remote Procedure Call,遠程過程調用)和HTTP調用的區別,不都是寫一個服務而後在客戶端調用麼?這裏請容許我迷之一笑~Naive!本文簡單地介紹一下兩種形式的C/S架構,先說一下他們最本質的區別,就是RPC主要是基於TCP/IP協議的,而HTTP服務主要是基於HTTP協議的,咱們都知道HTTP協議是在傳輸層協議TCP之上的,因此效率來看的話,RPC固然是要更勝一籌啦!下面來具體說一說RPC服務和HTTP服務。javascript

爲何RPC呢?就是沒法在一個進程內,甚至一個計算機內經過本地調用的方式完成的需求,好比好比不一樣的系統間的通信,甚至不一樣的組織間的通信。因爲計算能力須要橫向擴展,須要在多臺機器組成的集羣上部署應用。
RPC的協議有不少,好比最先的CORBA,Java RMI,Web Service的RPC風格,Hessian,Thrift,甚至Rest API。css

關於RPC
RPC框架,首先了解什麼叫RPC,爲何要RPC,RPC是指遠程過程調用,也就是說兩臺服務器A,B,一個應用部署在A服務器上,想要調用B服務器上應用提供的函數/方法,因爲不在一個內存空間,不能直接調用,須要經過網絡來表達調用的語義和傳達調用的數據。
html

 


好比說,一個方法多是這樣定義的:
Employee getEmployeeByName(String fullName)
那麼:java

    • 首先,要解決通信的問題,主要是經過在客戶端和服務器之間創建TCP鏈接,遠程過程調用的全部交換的數據都在這個鏈接裏傳輸。鏈接能夠是按需鏈接,調用結束後就斷掉,也能夠是長鏈接,多個遠程過程調用共享同一個鏈接。
    • 第二,要解決尋址的問題,也就是說,A服務器上的應用怎麼告訴底層的RPC框架,如何鏈接到B服務器(如主機或IP地址)以及特定的端口,方法的名稱名稱是什麼,這樣才能完成調用。好比基於Web服務協議棧的RPC,就要提供一個endpoint URI,或者是從UDDI服務上查找。若是是RMI調用的話,還須要一個RMI Registry來註冊服務的地址。
    • 第三,當A服務器上的應用發起遠程過程調用時,方法的參數須要經過底層的網絡協議如TCP傳遞到B服務器,因爲網絡協議是基於二進制的,內存中的參數的值要序列化成二進制的形式,也就是序列化(Serialize)或編組(marshal),經過尋址和傳輸將序列化的二進制發送給B服務器。
    • 第四,B服務器收到請求後,須要對參數進行反序列化(序列化的逆操做),恢復爲內存中的表達方式,而後找到對應的方法(尋址的一部分)進行本地調用,而後獲得返回值。
    • 第五,返回值還要發送回服務器A上的應用,也要通過序列化的方式發送,服務器A接到後,再反序列化,恢復爲內存中的表達方式,交給A服務器上的應用

OSI網絡七層模型

在說RPC和HTTP的區別以前,我覺的有必要了解一下OSI的七層網絡結構模型(雖然實際應用中基本上都是五層),它能夠分爲如下幾層: (從上到下)ios

  • 第一層:應用層。定義了用於在網絡中進行通訊和傳輸數據的接口;
  • 第二層:表示層。定義不一樣的系統中數據的傳輸格式,編碼和解碼規範等;
  • 第三層:會話層。管理用戶的會話,控制用戶間邏輯鏈接的創建和中斷;
  • 第四層:傳輸層。管理着網絡中的端到端的數據傳輸;
  • 第五層:網絡層。定義網絡設備間如何傳輸數據;
  • 第六層:鏈路層。將上面的網絡層的數據包封裝成數據幀,便於物理層傳輸;
  • 第七層:物理層。這一層主要就是傳輸這些二進制數據。

實際應用過程當中,五層協議結構裏面是沒有表示層和會話層的。應該說它們和應用層合併了。咱們應該將重點放在應用層和傳輸層這兩個層面。由於HTTP是應用層協議,而TCP是傳輸層協議。好,知道了網絡的分層模型之後咱們能夠更好地理解爲何RPC服務相比HTTP服務要Nice一些!程序員

RPC服務

從三個角度來介紹RPC服務:分別是RPC架構,同步異步調用以及流行的RPC框架。web

RPC架構

先說說RPC服務的基本架構吧。容許我可恥地盜一幅圖哈~咱們能夠很清楚地看到,一個完整的RPC架構裏面包含了四個核心的組件,分別是Client ,Server,Client Stub以及Server Stub,這個Stub你們能夠理解爲存根。分別說說這幾個組件:ajax

  • 客戶端(Client),服務的調用方。
  • 服務端(Server),真正的服務提供者。
  • 客戶端存根,存放服務端的地址消息,再將客戶端的請求參數打包成網絡消息,而後經過網絡遠程發送給服務方。
  • 服務端存根,接收客戶端發送過來的消息,將消息解包,並調用本地的方法。

RPC主要是用在大型企業裏面,由於大型企業裏面系統繁多,業務線複雜,並且效率優點很是重要的一塊,這個時候RPC的優點就比較明顯了。實際的開發當中是這麼作的,項目通常使用maven來管理。好比咱們有一個處理訂單的系統服務,先聲明它的全部的接口(這裏就是具體指Java中的interface),而後將整個項目打包爲一個jar包,服務端這邊引入這個二方庫,而後實現相應的功能,客戶端這邊也只須要引入這個二方庫便可調用了。爲何這麼作?主要是爲了減小客戶端這邊的jar包大小,由於每一次打包發佈的時候,jar包太多老是會影響效率。另外也是將客戶端和服務端解耦,提升代碼的可移植性。spring

同步調用與異步調用

什麼是同步調用?什麼是異步調用?同步調用就是客戶端等待調用執行完成並返回結果。異步調用就是客戶端不等待調用執行完成返回結果,不過依然能夠經過回調函數等接收到返回結果的通知。若是客戶端並不關心結果,則能夠變成一個單向的調用。這個過程有點相似於Java中的callablerunnable接口,咱們進行異步執行的時候,若是須要知道執行的結果,就可使用callable接口,而且能夠經過Future類獲取到異步執行的結果信息。若是不關心執行的結果,直接使用runnable接口就能夠了,由於它不返回結果,固然啦,callable也是能夠的,咱們不去獲取Future就能夠了。編程

流行的RPC框架

目前流行的開源RPC框架仍是比較多的。下面重點介紹三種:

  1. gRPC是Google最近公佈的開源軟件,基於最新的HTTP2.0協議,並支持常見的衆多編程語言。 咱們知道HTTP2.0是基於二進制的HTTP協議升級版本,目前各大瀏覽器都在馬不停蹄的加以支持。 這個RPC框架是基於HTTP協議實現的,底層使用到了Netty框架的支持。
  2. Thrift是Facebook的一個開源項目,主要是一個跨語言的服務開發框架。它有一個代碼生成器來對它所定義的IDL定義文件自動生成服務代碼框架。用戶只要在其以前進行二次開發就行,對於底層的RPC通信等都是透明的。不過這個對於用戶來講的話須要學習特定領域語言這個特性,仍是有必定成本的。
  3. Dubbo是阿里集團開源的一個極爲出名的RPC框架,在不少互聯網公司和企業應用中普遍使用。協議和序列化框架均可以插拔是及其鮮明的特點。一樣 的遠程接口是基於Java Interface,而且依託於spring框架方便開發。能夠方便的打包成單一文件,獨立進程運行,和如今的微服務概念一致。

偷偷告訴你集團內部已經不怎麼使用dubbo啦,如今用的比較多的叫HSF,又名「好舒服」。後面有可能會開源,你們拭目以待。

HTTP服務

其實在好久之前,我對於企業開發的模式一直定性爲HTTP接口開發,也就是咱們常說的RESTful風格的服務接口。的確,對於在接口很少、系統與系統交互較少的狀況下,解決信息孤島初期常使用的一種通訊手段;優勢就是簡單、直接、開發方便。利用現成的http協議進行傳輸。咱們記得以前本科實習在公司作後臺開發的時候,主要就是進行接口的開發,還要寫一大份接口文檔,嚴格地標明輸入輸出是什麼?說清楚每個接口的請求方法,以及請求參數須要注意的事項等。好比下面這個例子:
POST http://www.httpexample.com/restful/buyer/info/share
接口可能返回一個JSON字符串或者是XML文檔。而後客戶端再去處理這個返回的信息,從而能夠比較快速地進行開發。可是對於大型企業來講,內部子系統較多、接口很是多的狀況下,RPC框架的好處就顯示出來了,首先就是長連接,沒必要每次通訊都要像http同樣去3次握手什麼的,減小了網絡開銷;其次就是RPC框架通常都有註冊中心,有豐富的監控管理;發佈、下線接口、動態擴展等,對調用方來講是無感知、統一化的操做。

 

引自:https://blog.csdn.net/kkkloveyou/article/details/51874354

本文介紹了什麼是遠程過程調用(RPC),RPC 有哪些經常使用的方法,RPC 經歷了哪些發展階段,以及比較了各類 RPC 技術的優劣。

什麼是 RPC

RPC 是遠程過程調用(Remote Procedure Call)的縮寫形式,Birrell 和 Nelson 在 1984 發表於 ACM Transactions on Computer Systems 的論文《Implementing remote procedure calls》對 RPC 作了經典的詮釋。RPC 是指計算機 A 上的進程,調用另一臺計算機 B 上的進程,其中 A 上的調用進程被掛起,而 B 上的被調用進程開始執行,當值返回給 A 時,A 進程繼續執行。調用方能夠經過使用參數將信息傳送給被調用方,然後能夠經過傳回的結果獲得信息。而這一過程,對於開發人員來講是透明的。

圖1 描述了數據報在一個簡單的RPC傳遞的過程

圖1 描述了數據報在一個簡單的RPC傳遞的過程

注:上述論文,能夠在線閱讀 http://www.cs.virginia.edu/~zaher/classes/CS656/birrel.pdf

遠程過程調用採用客戶機/服務器(C/S)模式。請求程序就是一個客戶機,而服務提供程序就是一臺服務器。和常規或本地過程調用同樣,遠程過程調用是同步操做,在遠程過程結果返回以前,須要暫時停止請求程序。使用相同地址空間的低權進程或低權線程容許同時運行多個遠程過程調用。

RPC 的基本操做

讓咱們看看本地過程調用是如何實現的。考慮下面的 C 語言的調用:

count = read(fd, buf, nbytes);
  • 1

其中,fd 爲一個整型數,表示一個文件。buf 爲一個字符數組,用於存儲讀入的數據。 nbytes 爲另外一個整型數,用於記錄實際讀入的字節數。若是該調用位於主程序中,那麼在調用以前堆棧的狀態如圖2(a)所示。爲了進行調用,調用方首先把參數反序壓入堆棧,即爲最後一個參數先壓入,如圖2(b)所示。在 read 操做運行完畢後,它將返回值放在某個寄存器中,移出返回地址,並將控制權交回給調用方。調用方隨後將參數從堆棧中移出,使堆棧還原到最初的狀態。

圖2 過程調用中的參數傳遞

圖2 過程調用中的參數傳遞

RPC 背後的思想是儘可能使遠程過程調用具備與本地調用相同的形式。假設程序須要從某個文件讀取數據,程序員在代碼中執行 read 調用來取得數據。在傳統的系統中, read 例程由連接器從庫中提取出來,而後連接器再將它插入目標程序中。 read 過程是一個短過程,通常經過執行一個等效的 read 系統調用來實現。即,read 過程是一個位於用戶代碼與本地操做系統之間的接口。

雖然 read 中執行了系統調用,但它自己依然是經過將參數壓入堆棧的常規方式調用的。如圖2(b)所示,程序員並不知道 read 幹了啥。

RPC 是經過相似的途徑來得到透明性。當 read 其實是一個遠程過程時(好比在文件服務器所在的機器上運行的過程),庫中就放入 read 的另一個版本,稱爲客戶存根(client stub)。這種版本的 read 過程一樣遵循圖2(b)的調用次序,這點與原來的 read 過程相同。另外一個相同點是其中也執行了本地操做系統調用。惟一不一樣點是它不要求操做系統提供數據,而是將參數打包成消息,然後請求此消息發送到服務器,如圖3所示。在對 send 的調用後,客戶存根調用 receive 過程,隨即阻塞本身,直到收到響應消息。

圖3 客戶與服務器之間的RPC原理

圖3 客戶與服務器之間的RPC原理

當消息到達服務器時,服務器上的操做系統將它傳遞給服務器存根(server stub)。服務器存根是客戶存根在服務器端的等價物,也是一段代碼,用來將經過網絡輸入的請求轉換爲本地過程調用。服務器存根通常先調用 receive ,而後被阻塞,等待消息輸入。收到消息後,服務器將參數由消息中提取出來,而後以常規方式調用服務器上的相應過程(如圖3所示)。從服務器角度看,過程好像是由客戶直接調用的同樣:參數和返回地址都位於堆棧中,一切都很正常。服務器執行所要求的操做,隨後將獲得的結果以常規的方式返回給調用方。以 read 爲例,服務器將用數據填充 read 中第二個參數指向的緩衝區,該緩存區是屬於服務器存根內部的。

調用完後,服務器存根要將控制權教會給客戶發出調用的過程,它將結果(緩衝區)打包成消息,隨後調用 send 將結果返回給客戶。過後,服務器存根通常會再次調用 receive,等待下一個輸入的請求。

客戶機器接收到消息後,客戶操做系統發現該消息屬於某個客戶進程(實際上該進程是客戶存根,知識操做系統沒法區分兩者)。操做系統將消息複製到相應的緩存區中,隨後解除對客戶進程的阻塞。客戶存根檢查該消息,將結果提取出來並複製給調用者,然後以一般的方式返回。當調用者在 read 調用進行完畢後從新得到控制權時,它所知道的惟一事就是已經獲得了所需的數據。它不指導操做是在本地操做系統進行,仍是遠程完成。

整個方法,客戶方能夠簡單地忽略不關心的內容。客戶所涉及的操做只是執行普通的(本地)過程調用來訪問遠程服務,它並不須要直接調用 send 和 receive 。消息傳遞的全部細節都隱藏在雙方的庫過程當中,就像傳統庫隱藏了執行實際系統調用的細節同樣。

概況來講,遠程過程調用包含以下步驟:

  1. 客戶過程以正常的方式調用客戶存根;
  2. 客戶存根生成一個消息,而後調用本地操做系統;
  3. 客戶端操做系統將消息發送給遠程操做系統;
  4. 遠程操做系統將消息交給服務器存根;
  5. 服務器存根調將參數提取出來,然後調用服務器;
  6. 服務器執行要求的操做,操做完成後將結果返回給服務器存根;
  7. 服務器存根將結果打包成一個消息,然後調用本地操做系統;
  8. 服務器操做系統將含有結果的消息發送給客戶端操做系統;
  9. 客戶端操做系統將消息交給客戶存根;
  10. 客戶存根將結果從消息中提取出來,返回給調用它的客戶存根。

以上步驟就是將客戶過程對客戶存根發出的本地調用轉換成對服務器過程的本地調用,而客戶端和服務器都不會意識到中間步驟的存在。

RPC 的主要好處是雙重的。首先,程序員可使用過程調用語義來調用遠程函數並獲取響應。其次,簡化了編寫分佈式應用程序的難度,由於 RPC 隱藏了全部的網絡代碼存根函數。應用程序沒必要擔憂一些細節,好比 socket、端口號以及數據的轉換和解析。在 OSI 參考模型,RPC 跨越了會話層和表示層。

實現遠程過程調用

要實現遠程過程調用,需考慮如下幾個問題。

如何傳遞參數

傳遞值參數

傳遞值參數比較簡單,下圖圖展現了一個簡單 RPC 進行遠程計算的例子。其中,遠程過程 add(i,j) 有兩個參數 i 和 j, 其結果是返回 i 和 j 的算術和。

圖4 經過RPC進行遠程計算的步驟

圖4 經過RPC進行遠程計算的步驟

經過 RPC 進行遠程計算的步驟有:

  1. 將參數放入消息中,並在消息中添加要調用的過程的名稱或者編碼。
  2. 消息到達服務器後,服務器存根堆該消息進行分析,以判明須要調用哪一個過程,隨後執行相應的調用。
  3. 服務器運行完畢後,服務器存根將服務器獲得的結果打包成消息送回客戶存根,客戶存根將結果從消息中提取出來,把結果值返回給客戶端。

固然,這裏只是作了簡單的演示,在實際分佈式系統中,還須要考慮其餘狀況,由於不一樣的機器對於數字、字符和其餘類型的數據項的表示方式常有差別。好比整數型,就有 Big Endian 和 Little Endian 之分。

傳遞引用參數

傳遞引用參數相對來講比較困難。單純傳遞參數的引用(也包含指針)是徹底沒有意義的,由於引用地址傳遞給遠程計算機,其指向的內存位置可能跟遠程系統上徹底不一樣。若是你想支持傳遞引用參數,你必須發送參數的副本,將它們放置在遠程系統內存中,向他們傳遞一個指向服務器函數的指針,而後將對象發送回客戶端,複製它的引用。若是遠程過程調用必須支持引用複雜的結構,好比樹和鏈表,他們須要將結構複製到一個無指針的表示裏面(好比,一個扁平的樹),並傳輸到在遠程端來重建數據結構。

如何表示數據

在本地系統上不存在數據不相容的問題,由於數據格式老是相同的。而在分佈式系統中,不一樣遠程機器上可能有不一樣的字節順序,不一樣大小的整數,以及不一樣的浮點表示。對於 RPC,若是想與異構系統通訊,咱們就須要想出一個「標準」來對全部數據類型進行編碼,並能夠做爲參數傳遞。例如,ONC RPC 使用 XDR (eXternal Data Representation) 格式 。這些數據表示格式可使用隱式或顯式類型。隱式類型,是指只傳遞值,而不傳遞變量的名稱或類型。常見的例子是 ONC RPC 的 XDR 和 DCE RPC 的 NDR。顯式類型,指須要傳遞每一個字段的類型以及值。常見的例子是 ISO 標準 ASN.1 (Abstract Syntax Notation)、JSON (JavaScript Object Notation)、Google Protocol Buffers、以及各類基於 XML 的數據表示格式。

如何選用傳輸協議

有些實現只容許使用一個協議(例如 TCP )。大多數 RPC 實現支持幾個,並容許用戶選擇。

出錯時,會發生什麼

相比於本地過程調用,遠程過程調用出錯的機會將會更多。因爲本地過程調用沒有過程調用失敗的概念,項目使用遠程過程調用必須準備測試遠程過程調用的失敗或捕獲異常。

遠程調用的語義是什麼

調用一個普通的過程語義很簡單:當咱們調用時,過程被執行。遠程過程徹底一次性調用成功是很是難以實現。執行遠程過程能夠有以下結果:

  • 若是服務器崩潰或進程在運行服務器代碼以前就死了,那麼遠程過程會被執行0次;
  • 若是一切工做正常,遠程過程會被執行1次;
  • 若是服務器返回服務器存根後在發送響應前就奔潰了,遠程過程會被執行1次或者屢次。客戶端接收不到返回的響應,能夠決定再試一次,所以出現屢次執行函數。若是沒有再試一次,函數執行一次;
  • 若是客戶機超時和從新傳輸,那麼遠程過程會被執行屢次。也有多是原始請求延遲了。二者均可能會執行或不執行。

RPC 系統一般會提供至少一次或最多一次的語義,或者在二者之間選擇。若是須要了解應用程序的性質和遠程過程的功能是否安全,能夠經過屢次調用同一個函數來驗證。若是一個函數能夠運行任何次數而不影響結果,這是冪等(idempotent)函數的,如天天的時間、數學函數、讀取靜態數據等。不然,它是一個非冪等(nonidempotent)函數,如添加或修改一個文件)。

遠程調用的性能怎麼樣

毫無疑問,一個遠程過程調用將會比常規的本地過程調用慢得多,由於產生了額外的步驟以及網絡傳輸自己存在延遲。然而,這並不該該阻止咱們使用遠程過程調用。

遠程調用安全嗎?

使用 RPC,咱們必須關注各類安全問題:

  • 客戶端發送消息到遠程過程,那個過程是可信的嗎?
  • 客戶端發送消息到遠程計算機,那個遠程機器是可信的嗎?
  • 服務器如何驗證接收的消息是來自合法的客戶端嗎?服務器如何識別客戶端?
  • 消息在網絡中傳播如何防止時被其餘進程嗅探?
  • 能夠由其餘進程消息被攔截和修改時遍歷網絡從客戶端到服務器或服務器端?
  • 協議能防止重播攻擊嗎?
  • 如何防止消息在網絡傳播中被意外損壞或截斷?

遠程過程調用的優勢

遠程過程調用有諸多的優勢:

  • 你沒必要擔憂傳輸地址問題。服務器能夠綁定到任何可用的端口,而後用 RPC 名稱服務來註冊端口。客戶端將經過該名稱服務來找到對應的端口號所須要的程序。而這一切對於程序員來講是透明的。
  • 系統能夠獨立於傳輸提供者。自動生成服務器存根使其能夠在系統上的任何一個傳輸提供者上可用,包括 TCP 和 UDP,而這些,客戶端能夠動態選擇的。當代碼發送之後,接收消息是自動生成的,而不須要額外的編程代碼。
  • 應用程序在客戶端只須要知道一個傳輸地址——名稱服務,負責告訴應用程序去哪裏鏈接服務器函數集。
  • 使用函數調用模型來代替 socket 的發送/接收(讀/寫)接口。用戶不須要處理參數的解析。

RPC API

任何 RPC 實現都須要提供一組支持庫。這些包括:

  • 名稱服務操做: 
    註冊和查找綁定信息(端口、機器)。容許一個應用程序使用動態端口(操做系統分配的);
  • 綁定操做:使用適當的協議創建客戶機/服務器通訊(創建通訊端點);
  • 終端操做:註冊端點信息(協議、端口號、機器名)到名稱服務並監聽過程調用請求。這些函數一般被自動生成的主程序——服務器存根(骨架)所調用;
  • 安全操做:系統應該提供機制保證客戶端和服務器之間可以相互驗證,二者之間提供一個安全的通訊通道;
  • 國際化操做(可能):這是不多的一部分 RPC 包可能包括了轉換時間格式、貨幣格式和特定於語言的在字符串表的字符串的功能;
  • 封送處理/數據轉換操做:函數將數據序列化爲一個普通的的字節數組,經過網絡進行傳遞,並可以重建;
  • 存根內存管理和垃圾收集:存根可能須要分配內存來存儲參數,特別是模擬引用傳遞語義。RPC 包須要分配和清理任何這樣的分配。他們也可能須要爲建立網絡緩衝區而分配內存。RPC 包支持對象,RPC 系統須要一種跟蹤遠程客戶端是否仍有引用對象或一個對象是否能夠刪除。
  • 程序標識操做:容許應用程序訪問(或處理) RPC 接口集的標識符,這樣的服務器提供的接口集能夠被用來交流和使用。
  • 對象和函數的標識操做: 
    容許將遠程函數或遠程對象的引用傳遞給其餘進程。並非全部的 RPC 系統都支持。

因此,判斷一種通訊方式是不是 RPC,就看它是否提供上述的 API。

第一代 RPC

ONC RPC(之前稱爲 Sun RPC)

Sun 公司是第一個提供商業化 RPC 庫和 RPC 編譯器。在1980年代中期 Sun 計算機提供 RPC,並在 Sun Network File System(NFS) 獲得支持。該協議被主要以 Sun 和 AT&T 爲首的 Open Network Computing (開放網絡計算)做爲一個標準來推進。這是一個很是輕量級 RPC 系統可用在大多數 POSIX 和類 POSIX 操做系統中使用,包括 Linux、SunOS、OS X 和各類發佈版本的 BSD。這樣的系統被稱爲 Sun RPC 或 ONC RPC。

ONC RPC 提供了一個編譯器,須要一個遠程過程接口的定義來生成客戶機和服務器的存根函數。這個編譯器叫作 rpcgen。在運行此編譯器以前,程序員必須提供接口定義。包含函數聲明的接口定義,經過版本號進行分組,並被一個獨特的程序編碼來標識。該程序編碼可以讓客戶來肯定所需的接口。版本號是很是有用的,即便客戶沒有更新到最新的代碼仍然能夠鏈接到一個新的服務器,只要該服務器還支持舊接口。

參數經過網絡轉化成一種隱式類型序列化格式被稱爲 XDR (eXternal Data Representation)。這將確保參數可以發送到異構系統能夠被正常使用,及時這些系統可能使用了不一樣的字節順序,不一樣大小的整數,或不一樣的浮點或字符串表示。最後,Sun RPC 提供了一個實現必要的支持 RPC 協議和 socket 例程的運行時庫。

全部的程序員都須要寫是一個客戶端程序(client.c),服務器功能(server.c)和 RPC 接口定義(date.x)。當 RPC 接口定義(後綴爲.x 的文件,例如 date.x)是用 rpcgen 編譯的,會建立三個或四個文件。下面是 date.x 的例子:

  • date.h:包含項目的定義、版本和聲明的函數。客戶端和服務器端功能應該包括這個文件。
  • date_svc.c :C 語言代碼來實現服務器存根。
  • date_clnt.c :C 語言代碼來實現客戶端存根。
  • date_xdr.c :包含 XDR 例程來將數據轉化爲 XDR 格式。若是這個文件生成,它應該編譯並用來連接在客戶端和服務器的函數。

建立客戶端和服務器可執行文件的第一步是定義在文件 date.x 裏的編譯數據。以後,客戶端和服務器端函數可能被編譯,並連接各自 rpcgen 生成的存根函數。

在舊版本里,傳輸協議只能將字符串「tcp」或字符串「udp」來指定各自的 IP 服務 RPC,且僅限於 Linux 實現的 RPC。爲了使接口更加靈活,UNIX 系統從版本 4 (SunOS 從版本 5)開始網絡選擇程序容許一個更普通的規範。他們搜索文件(/etc/netconfig),來查找第一個知足您需求的提供者。最後一個參數能夠是:

  • 「netpath」:搜索 NETPATH 環境變量用於首選傳輸提供者的序列;
  • 「circuit_n」:找到第一個在 NETPATH 列表中的虛擬電路提供者;
  • 「datagram_n」:找到第一個 NETPATH 列表中的數據報提供者;
  • 「visible」:找到第一個在 /etc/netconfig 的可見傳輸提供者;
  • 「circuit_v」:找到第一個在 /etc/netconfig 的可見虛擬電路傳輸提供者;
  • 「datagram_v」:找到第一個在 /etc/netconfig 的可見數據報傳輸提供者;

每一個遠程過程調用最初僅限於接受一個輸入參數。系統只是後來修改成支持多個參數。支持單一參數 RPC 在一些 rpcgen 的版本中仍然是默認的,好比蘋果的 OS X。傳遞多個參數必須經過定義一個結構,包含所需的參數,初始化它,並傳遞這個結構。

遠程過程調用返回一個指針指向結果而不是指望的結果。服務器函數必須修改來能接受一個 RPC 定義(.x 文件)中聲明的值的指針做爲輸入,並返回一個結果值的指針。在服務器上,一個指針必須是指向靜態數據的指針。不然,當過程返回或釋放過程的框架所指出的區域將未定義。在客戶端,返回指針可讓咱們區分一個失敗的 RPC(空指針)和一個空返回從服務器(間接空指針)。

RPC 過程的名稱若在 RPC 定義文件中作了定義,則會轉換爲小寫,並在後綴價下劃線,後跟一個版本號。例如,BIN_DATE 轉成爲引用函數 bin_date_1 。您的服務器必須實現 bin_date_1。

當咱們運行這個程序時,會發生什麼?

服務器

當咱們啓動服務器,服務器存根代碼(程序)在後臺運行運行。它建立一個 socket 並可綁定任何本地端口到 socket。而後調用一個在 RPC 庫的函數 svc_register,來註冊程序編號和版本。這個是用來聯繫 port mapper(端口映射器)。port mapper 是一個獨立的進程,一般是在系統啓動時啓動。它跟蹤端口號、版本號以及程序編號。在 UNIX 系統版本4中,這個進程稱爲 rpcbind。在 Linux 、OS X 和 BSD 系統,它被稱爲 portmap。

圖5 ONC RPC 中的函數查找

圖5 ONC RPC 中的函數查找

客戶端

當咱們開始客戶端程序時,它首先用遠程系統的名稱、程序編號、版本號和協議來調用 clnt_create 。它接觸遠程系統上的端口映射器,來爲系統找到合適的端口。

而後客戶端調用 RPC 存根函數(在本例中爲 bin_date_1)。該函數發送一條消息(如,數據報)到服務器(使用早些時候發現的端口號)並等待響應。對於數據報服務來講,若沒有接收到響應,它將從新發送一個固定的次數請求。

消息接着被遠程系統接收到,它調用服務器函數(bin_date_1)並將返回值返回給客戶端存根。客戶端存根然後返回到客戶端發出調用的代碼。

分佈式計算環境中的 RPC(DCE RPC)

DCE(Distributed Computing Environment,分佈式計算環境)是一組由OFS(Open Software Foundation,開放軟件基金會)設計的組件,用來提供支持分佈式應用和分佈式環境。與 X/Open 合併後,這組織成爲了 The Open Group (開放式開發組)。DCE 提供的組件包括一個分佈式文件服務、時間服務、目錄服務以及其餘服務。固然,咱們感興趣的是 DCE 的遠程過程調用。它很是相似於 Sun RPC。接口是由 Interface Definition Notation (IDN) 定義的。相似於 Sun RPC,接口定義就像函數原型。

Sun RPC 不足之處在於,服務器的標識是一個「獨特」的 32-bit 數字。雖然這是一個比在 socket 中 16-bit 可用空間更大的空間,但仍然沒法知足數字惟一性的需求。DCE RPC 考慮到了這一缺陷,它無需程序員來處理編碼。在編寫應用程序時的第一步是從 uuidgen 程序得到一個唯一的 ID。這個程序會生成一個包含 ID 接口的原型 IDN 文件,並保證永遠不會再次使用。它是一個 128-bit 的值,其中包含一個位置代碼和建立時間的編碼。而後用戶編輯原型文件,填寫遠程過程聲明。

在這一步後,IDN 的編譯器 dceidl(相似於 rpcgen)會生成一個頭、客戶機存根和服務器存根。

Sun RPC 的另外一個缺陷是,客戶端必須知道服務器在哪臺機器上。當它要訪問時,必需要詢問機器上的 RPC 名稱服務程序編碼所對應的端口號。DCE 支持將多個機器組織成爲管理實體,稱爲 cells。cell 目錄服務器使得每臺機器知道如何與另一臺負責維護 cell 信息服務機器交互。

在 Sun RPC 中,服務器只能用本地名稱服務(端口映射器)來註冊其程序編號到端口映射。而在 DCE 中,服務器用 RPC 守護進程(名稱服務器)來註冊其端點(端口)到本地機器,而且用 cell 目錄服務器註冊其程序名字到機器的映射。當客戶機想要與一個 RPC 服務器創建通訊,它首先要求其 cell 目錄服務器來定位服務器所在的機器。而後客戶端從 RPC 守護進程處得到機器上服務器進程的端口號。DCE 的跨 cell 還支持更復雜的搜索。

DCE RPC 定義了 NDR (Network Data Representation) 用於對網絡進行編碼來封送信息。與用一個單一的規範來表示不一樣的數據類型相比,NDR 支持多規範(multi-canonical)格式。容許客戶端來選擇使用哪一種格式,理想的狀況是不須要將它從本地類型來轉換。若是這不一樣於服務器的本地數據表示,服務器將仍然須要轉換,但多規範格式能夠避免當客戶端和服務器都共享相同的本地格式的狀況下轉換爲其餘外部格式。例如,在一個規定了大端字節序網絡數據格式的狀況下,客戶端和服務器只支持小端字節序,那麼客戶端必須將每一個數據從小端字節序轉爲大端字節序,而當服務器接受到消息後,將每一個數據轉回小端字節序。多規範網絡數據表示將容許客戶端發送網絡消息包含小端字節序格式的數據。

圖6  DCE RPC 中的函數查找

圖6 DCE RPC 中的函數查找

第二代 RPC:支持對象

面向對象的語言開始在1980年代末興起,很明顯,當時的 Sun ONC 和 DCE RPC 系統都沒有提供任何支持諸如從遠程類實例化遠程對象、跟蹤對象的實例或提供支持多態性。現有的 RPC 機制雖然能夠運做,但他們仍然不支持自動、透明的方式的面向對象編程技術。

微軟 DCOM(COM+)

1992年4月,微軟發佈 Windows 3.1 包括一種機制稱爲 OLE (Object Linking and Embedding)。這容許一個程序動態連接其餘庫來支持的其餘功能。如將一個電子表格嵌入到 Word 文檔。OLE 演變成了 COM (Component Object Model)。一個 COM 對象是一個二進制文件。使用 COM 服務的程序來訪問標準化接口的 COM 對象而不是其內部結構。COM 對象用全局惟一標識符(GUID)來命名,用類的 ID 來識別對象的類。幾種方法來建立一個 COM 對象(例如 CoGetInstanceFromFile)。COM 庫在系統註冊表中查找相應的二進制代碼(一個 DLL 或可執行文件),來建立對象,並給調用者返回一個接口指針。COM 的着眼點是在於同一臺計算機上不一樣應用程序之間的通信需求.

DCOM( Distributed Component Object Model)是 COM 的擴展,它支持不一樣的兩臺機器上的組件間的通訊,並且不論它們是運行在局域網、廣域網、仍是 Internet 上。藉助 DCOM 你的應用程序將可以進行任意空間分佈。DCOM 於1996年在 Windows NT4.0 中引入的,後來改名爲 COM+。因爲 DCOM 是爲了支持訪問遠程 COM 對象,須要建立一個對象的過程,此時須要提供服務器的網絡名以及類 ID。微軟提供了一些機制來實現這一點。最透明的方式是遠程計算機的名稱固定在註冊表(或 DCOM 類存儲)裏,與特定類 ID 相關聯。以此方式,應用程序不知道它正在訪問一個遠程對象,而且可使用與訪問本地 COM 對象相同的接口指針。另外一方面,應用程序也可指定一個機器名做爲參數。

因爲 DCOM 是 COM 這個組件技術的無縫升級,因此你可以從你現有的有關 COM 得知識中獲益,你的之前在 COM 中開發的應用程序、組件、工具均可以移入分佈式的環境中。DCOM 將爲你屏蔽底層網絡協議的細節,你只須要集中精力於你的應用。

DCOM 最大的缺點是這是微軟獨家的解決辦法,在跨防火牆方面的工做作得不是很好(大多數RPC系統也有相似的問題),由於防火牆必須容許某些端口來容許 ORPC 和 DCOM 經過。

CORBA

雖然 DCE 修復的一些 Sun RPC 的缺點,但某些缺陷依然存在。例如,若是服務器沒有運行,客戶端是沒法鏈接到遠程過程進行調用的。管理員必需要確保在任何客戶端試圖鏈接到服務器以前將服務器啓動。若是一個新服務或接口添加到了系統,客戶端是不能發現的。最後,面嚮對象語言指望在函數調用中體現多態性,即不一樣類型的數據的函數的行爲應該有所不一樣,而這點偏偏是傳統的 RPC 所不支持的。

CORBA (Common Object Request Broker Architecture) 就是爲了解決上面提到的各類問題。是由 OMG 組織制訂的一種標準的面向對象應用程 序體系規範。或者說 CORBA體系結構是對象管理組織(OMG)爲解決分佈式處理環境(DCE)中,硬件和軟件系統的互連而提出的一種解決方案。OMG 成立於1989年,做爲一個非營利性組織,集中致力於開發在技術上具備先進性、在商業上具備可行性而且獨立於廠商的軟件互聯規範,推廣面向對象模型技術,加強軟件的可移植性(Portability)、可重用性(Reusability)和互操做性(Interoperability)。該組織成立之初,成員包括 Unisys、Sun、Cannon、Hewlett-Packard 和 Philips 等在業界享有聲譽的軟硬件廠商,目前該組織擁有800多家成員。

CORBA 體系的主要內容包括如下幾部分:

  • 對象請求代理 (Object Request Broker,ORB):負責對象在分佈環境中透明地收發請求和響應,它是構建分佈對象應用、在異構或同構環境下實現應用間互操做的基礎。
  • 對象服務(Object Services):爲使用和實現對象而提供的基本對象集合,這些服務應獨立於應用領域。主要的 CORBA 服務有:名錄服務(Naming Service)、事件服務(Event Service)、生命週期服務(Life Cycle Service)、關係服務(Relationship Service)以及事務服務(Transaction Service)等。這些服務幾乎包括分佈系統和麪向對象系統的各個方面,每一個組成部分都很是複雜。
  • 公共設施(Common Facilitites):向終端用戶提供一組共享服務接口,例如系統管理、組合文檔和電子郵件等。
  • 應用接口(Application Interfaces)。由銷售商提供的可控制其接口的產品,相應於傳統的應用層表示,處於參考模型的最高層。
  • 領域接口(Domain Interfaces):爲應用領域服務而提供的接口,如OMG 組織爲 PDM 系統制定的規範。

當客戶端發出請求時,ORB 作了以下事情:

  • 在客戶端編組參數;
  • 定位服務器對象。若是有必要的話,它會在服務器建立一個過程來處理請求;
  • 若是服務器是遠程是,就使用 RPC 或 socket 來傳送請求;
  • 在服務器上將參數解析成爲服務器格式;
  • 在服務器上組裝返回值;
  • 若是服務器是遠程的,就將返回值傳回;
  • 在客戶端對返回結果進行解析;

IDL(Interface Definition Language) 是用於指定類的名字、屬性和方法。它不包含對象的實現。IDL 編譯器生成代碼來處理編組、解封以及ORB與網絡之間的交互。它會生成客戶機和服務器存根。IDL 是編程語言中立,支持包括C、C++、Java、Perl、Python、Ada、COBOL、Smalltalk、Objective C 和 LISP 等語言。一個示例IDL以下所示:

Module StudentObject {
    Struct StudentInfo {
        String name;
        int id; float gpa; }; exception Unknown {}; interface Student { StudentInfo getinfo(in string name) raises(unknown); void putinfo(in StudentInfo data); }; };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

IDL數據類型包括:

  • 基本類型:long, short, string, float, …
  • 構造類型:struct、union、枚舉、序列
  • 對象引用
  • any 類型:一個動態類型的值

編程中最多見的實現方式是經過對象引用來實現請求。下面是一個使用 IDL 的例子:

Student st = ... // get object reference try { StudentInfo sinfo = st.getinfo("Fred Grampp"); } catch (Throwable e) { ... // error }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在 CORBA 規範中,沒有明確說明不一樣廠商的中間件產品要實現全部的服務功能,而且容許廠商開發本身的服務類型。所以, 不一樣廠商的 ORB 產品對 CORBA 服務的支持能力不一樣,使咱們在針對待開發系統的功能進行中間件產品選擇時,有更多的選擇餘地。

CORBA 的不足有:

  • 儘管有多家供應商提供CORBA產品,可是仍找不到可以單獨爲異種網絡中的全部環境提供實現的供應商。不一樣的 CORBA 實現之間會出現缺少互操做性的現象,從而形成一些問題;並且,因爲供應商經常會自行定義擴展,而 CORBA 又缺少針對多線程環境的規範,對於像 C 或 C++ 這樣的語言,源碼兼容性並未徹底實現。
  • CORBA 過於複雜,要熟悉 CORBA,並進行相應的設計和編程,須要許多個月來掌握,而要達到專家水平,則須要好幾年。

更多有關 CORBA 的優缺點,能夠參閱 Michi Henning 的《The rise and fall of CORBA》。

Java RMI

CORBA 旨在提供一組全面的服務來管理在異構環境中(不一樣語言、操做系統、網絡)的對象。Java 在其最初只支持經過 socket 來實現分佈式通訊。1995年,做爲 Java 的締造者,Sun 公司開始建立一個 Java 的擴展,稱爲 Java RMI(Remote Method Invocation,遠程方法調用)。Java RMI 容許程序員建立分佈式應用程序時,能夠從其餘 Java 虛擬機(JVM)調用遠程對象的方法。

一旦應用程序(客戶端)引用了遠程對象,就能夠進行遠程調用了。這是經過 RMI 提供的命名服務(RMI 註冊中心)來查找遠程對象,來接收做爲返回值的引用。Java RMI 在概念上相似於 RPC,但能在不一樣地址空間支持對象調用的語義。

與大多數其餘諸如 CORBA 的 RPC 系統不一樣,RMI 只支持基於 Java 來構建,但也正是這個緣由, RMI 對於語言來講更加整潔,無需作額外的數據序列化工做。Java RMI 的設計目標應該是:

  • 可以適應語言、集成到語言、易於使用;
  • 支持無縫的遠程調用對象;
  • 支持服務器到 applet 的回調;
  • 保障 Java 對象的安全環境;
  • 支持分佈式垃圾回收;
  • 支持多種傳輸。

分佈式對象模型與本地 Java 對象模型類似點在於:

  • 引用一個對象能夠做爲參數傳遞或做爲返回的結果;
  • 遠程對象能夠投到任何使用 Java 語法實現的遠程接口的集合上;
  • 內置 Java instanceof 操做符能夠用來測試遠程對象是否支持遠程接口。

不一樣點在於:

  • 遠程對象的類是與遠程接口進行交互,而不是與這些接口的實現類交互;
  • Non-remote 參數對於遠程方法調用來講是經過複製,而不是經過引用;
  • 遠程對象是經過引用來傳遞,而不是複製實際的遠程實現;
  • 客戶端必須處理額外的異常。

接口和類

全部的遠程接口都繼承自 java.rmi.Remote 接口。例如:

public interface bankaccount extends Remote { public void deposit(float amount) throws java.rmi.RemoteException; public void withdraw(float amount) throws OverdrawnException, java.rmi.RemoteException; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

注意,每一個方法必須在 throws 裏面聲明 java.rmi.RemoteException 。 只要客戶端調用遠程方法出現失敗,這個異常就會拋出。

遠程對象類

Java.rmi.server.RemoteObject 類提供了遠程對象實現的語義包括hashCode、equals 和 toString。 java.rmi.server.RemoteServer 及其子類提供讓對象實現遠程可見。java.rmi.server.UnicastRemoteObject 類定義了客戶機與服務器對象實例創建一對一的鏈接.

存根

Java RMI 經過建立存根函數來工做。存根由 rmic 編譯器生成。自 Java 1.5 以來,Java 支持在運行時動態生成存根類。編譯器 rmic 會提供各類編譯選項。

定位對象

引導名稱服務提供了用於存儲對遠程對象的命名引用。一個遠程對象引用能夠存儲使用類 java.rmi.Naming 提供的基於 URL 的方法。例如,

BankAccount acct = new BankAcctImpl(); String url = "rmi://java.sun.com/account"; // bind url to remote object java.rmi.Naming.bind(url, acct); // look up account acct = (BankAccount)java.rmi.Naming.lookup(url);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

圖7 Java RMI 工做流程

圖7 Java RMI 工做流程

RMI 架構

RMI 是一個三層架構(圖8)。最上面是 Stub/Skeleton layer(存根/骨架層)。方法調用從 Stub、Remote Reference Layer (遠程引用層)和 Transport Layer(傳輸層)向下,傳遞給主機,而後再次經傳 Transport Layer 層,向上穿過 Remote Reference Layer 和 Skeleton ,到達服務器對象。 Stub 扮演着遠程服務器對象的代理的角色,使該對象可被客戶激活。Remote Reference Layer 處理語義、管理單一或多重對象的通訊,決定調用是應發往一個服務器仍是多個。Transport Layer 管理實際的鏈接,而且追蹤能夠接受方法調用的遠程對象。服務器端的 Skeleton 完成對服務器對象實際的方法調用,並獲取返回值。返回值向下經 Remote Reference Layer 、服務器端的 Transport Layer 傳遞迴客戶端,再向上經 Transport Layer 和 Remote Reference Layer 返回。最後,Stub 程序得到返回值。

要完成以上步驟須要有如下幾個步驟:

  • 生成一個遠程接口;
  • 實現遠程對象(服務器端程序);
  • 生成 Stub 和 Skeleton(服務器端程序);
  • 編寫服務器程序 ;
  • 編寫客戶程序 ;
  • 註冊遠程對象;
  • 啓動遠程對象

圖8 Java RMI 架構

圖8 Java RMI 架構

RMI 分佈式垃圾回收

根據 Java 虛擬機的垃圾回收機制原理,在分佈式環境下,服務器進程須要知道哪些對象再也不由客戶端引用,從而能夠被刪除(垃圾回收)。在 JVM中,Java 使用引用計數。當引用計數歸零時,對象將會垃圾回收。在RMI,Java 支持兩種操做:dirty 和 clean。本地 JVM 按期發送一個 dirty 到服務器來講明該對象仍在使用。按期重發 dirty 的週期是由服務器租賃時間來決定的。當客戶端沒有須要更多的本地引用遠程對象時,它發送一個 clean 調用給服務器。不像 DCOM,服務器不須要計算每一個客戶機使用的對象,只是簡單的作下通知。若是它租賃時間到期以前沒有接收到任何 dirty 或者 clean 的消息,則能夠安排將對象刪除。

第三代 RPC 以及 Web Services

因爲互聯網的興起,Web 瀏覽器成爲占主導地位的用於訪問信息的模型。如今的應用設計的首要任務大多數是提供用戶經過瀏覽器來訪問,而不是編程訪問或操做數據。

網頁設計關注的是內容。解析展示方面每每是繁瑣的。傳統 RPC 解決方案能夠工做在互聯網上,但問題是,他們一般嚴重依賴於動態端口分配,每每要進行額外的防火牆配置。

Web Services 成爲一組協議,容許服務被髮布、發現,並用於技術無關的形式。即服務不該該依賴於客戶的語言、操做系統或機器架構。

Web Services 的實現通常是使用 Web 服務器做爲服務請求的管道。客戶端訪問該服務,首先是經過一個 HTTP 協議發送請求到服務器上的 Web 服務器。Web 服務器配置識別 URL 的一部分路徑名或文件名後綴並將請求傳遞給特定的瀏覽器插件模塊。這個模塊能夠除去頭、解析數據(若是須要),並根據須要調用其餘函數或模塊。對於這個實現流,一個常見的例子是瀏覽器對於 Java Servlet 的支持。HTTP 請求會被轉發到 JVM 運行的服務端代碼來執行處理。

XML-RPC

XML-RPC 是1998年做爲一個 RPC 消息傳遞協議,將請求和響應封裝解析爲人類可讀的 XML格式。XML 格式基於 HTTP 協議,緩解了傳統企業的防火牆須要爲 RPC 服務器應用程序打開額外的端口的問題。

下面是一個 XML-RPC 消息的例子:

<methodCall> <methodName> sample.sumAndDifference </methodName> <params> <param><value><int> 5 </int></value></param> <param><value><int> 3 </int></value></param> </params> </methodCall>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

這個例子中,方法 sumAndDifference 有兩個整數參數 5 和 3。

XML-RPC 支持的基本數據類型是:int、string、boolean、double 和 dateTime.iso8601。此外,還有 base64 類型用於編碼任意二進制數據。array 和 struct 容許定義數組和結構。

XML-RPC 不限制語任何特定的語言,也不是一套完整的軟件來處理遠程過程,諸如存根生成、對象管理和服務查找都不在協議內。如今有不少庫針能夠針對不一樣的語言,好比 Apache XML-RPC 能夠用於 Java、Python 和 Perl。

XML-RPC 是一個簡單的規範(約7頁),沒有雄心勃勃的目標——它只關注消息,而並不處理諸如垃圾收集、遠程對象、遠程過程的名稱服務和其餘方面的問題。然而,即便沒有普遍的產業支持,簡單的協議卻能普遍採用。

SOAP

SOAP(Simple Object Access Protocol,簡單對象訪問協議),是以 XML-RPC 規範做爲建立 SOAP 的依據,成立於1998年,得到微軟和 IBM 的大力支持。該協議在建立初期只做爲一種對象訪問協議,但因爲 SOAP 的發展,其協議已經不單只是用於簡單的訪問對象,因此這種 SOAP 縮寫已經在標準的1.2版後被廢止了。1.2版在2003年6月24日成爲 W3C 的推薦版本。SOAP 指定 XML 做爲無狀態的消息交換格式,包括了 RPC 式的過程調用。

有關 SOAP 的標準能夠參閱 https://www.w3.org/TR/soap/

SOAP 只是一種消息格式,並未定義垃圾回收、對象引用、存根生成和傳輸協議。

下面是一個簡單的例子:

<?xml version='1.0' ?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> <env:Header> <m:reservation xmlns:m="http://travelcompany.example.org/reservation" env:role="http://www.w3.org/2003/05/soap-envelope/role/next" env:mustUnderstand="true"> <m:reference>uuid:093a2da1-q345-739r-ba5d-pqff98fe8j7d</m:reference> <m:dateAndTime>2001-11-29T13:20:00.000-05:00</m:dateAndTime> </m:reservation> <n:passenger xmlns:n="http://mycompany.example.com/employees" env:role="http://www.w3.org/2003/05/soap-envelope/role/next" env:mustUnderstand="true"> <n:name>Åke Jógvan Øyvind</n:name> </n:passenger> </env:Header> <env:Body> <p:itinerary xmlns:p="http://travelcompany.example.org/reservation/travel"> <p:departure> <p:departing>New York</p:departing> <p:arriving>Los Angeles</p:arriving> <p:departureDate>2001-12-14</p:departureDate> <p:departureTime>late afternoon</p:departureTime> <p:seatPreference>aisle</p:seatPreference> </p:departure> <p:return> <p:departing>Los Angeles</p:departing> <p:arriving>New York</p:arriving> <p:departureDate>2001-12-20</p:departureDate> <p:departureTime>mid-morning</p:departureTime> <p:seatPreference/> </p:return> </p:itinerary> <q:lodging xmlns:q="http://travelcompany.example.org/reservation/hotels"> <q:preference>none</q:preference> </q:lodging> </env:Body> </env:Envelope>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

其中<soap:Envelope>是 SOAP 消息中的根節點,是 SOAP 消息中必須的部分。<soap:Header>是 SOAP 消息中可選部分,是指消息頭。<soap:Body>是 SOAP 中必須部分,是指消息體。

上面例子的 SOAP 消息結構以下:

圖9 SOAP 消息結構

圖9 SOAP 消息結構

SOAP 它只是提供了一個標準化的消息結構,爲了實現它每每須要用 WSDL 來描述 Web Services 的方法。WSDL (Web Services Description Language) 是基於 XML 的一種用於描述 Web Services 以及如何訪問 Web Services 的語言。

WSDL 文檔包括如下幾個部分:

  • 類型(Types):定義了 Web Services 使用的數據類型;
  • 消息(n/a):描述使用消息的數據元素或參數;
  • 接口(Interface):描述服務提供的操做。這包括操做以及每一個操做所使用的輸入和輸出消息;
  • 綁定(Binding):爲每一個端口定義消息格式和協議細節。例如,它能夠定義 RPC 式的消息;
  • 服務(Service):系統功能相關的集合,包括其關聯的接口、操做、消息等;
  • 終點(Endpoint):定義了地址或者 Web Services 的鏈接點;
  • 操做(Operation):定義了 SOAP 的動做,以及消息編碼的方式。

下面是一個 WSDL 2.0 版本的例子:

<?xml version="1.0" encoding="UTF-8"?> <description xmlns="http://www.w3.org/ns/wsdl" xmlns:tns="http://www.tmsws.com/wsdl20sample" xmlns:whttp="http://schemas.xmlsoap.org/wsdl/http/" xmlns:wsoap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="http://www.tmsws.com/wsdl20sample"> <documentation> This is a sample WSDL 2.0 document. </documentation> <!-- Abstract type --> <types> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.tmsws.com/wsdl20sample" targetNamespace="http://www.example.com/wsdl20sample"> <xs:element name="request"> ... </xs:element> <xs:element name="response"> ... </xs:element> </xs:schema> </types> <!-- Abstract interfaces --> <interface name="Interface1"> <fault name="Error1" element="tns:response"/> <operation name="Get" pattern="http://www.w3.org/ns/wsdl/in-out"> <input messageLabel="In" element="tns:request"/> <output messageLabel="Out" element="tns:response"/> </operation> </interface> <!-- Concrete Binding Over HTTP --> <binding name="HttpBinding" interface="tns:Interface1" type="http://www.w3.org/ns/wsdl/http"> <operation ref="tns:Get" whttp:method="GET"/> </binding> <!-- Concrete Binding with SOAP--> <binding name="SoapBinding" interface="tns:Interface1" type="http://www.w3.org/ns/wsdl/soap" wsoap:protocol="http://www.w3.org/2003/05/soap/bindings/HTTP/" wsoap:mepDefault="http://www.w3.org/2003/05/soap/mep/request-response"> <operation ref="tns:Get" /> </binding> <!-- Web Service offering endpoints for both bindings--> <service name="Service1" interface="tns:Interface1"> <endpoint name="HttpEndpoint" binding="tns:HttpBinding" address="http://www.example.com/rest/"/> <endpoint name="SoapEndpoint" binding="tns:SoapBinding" address="http://www.example.com/soap/"/> </service> </description>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

Microsoft .NET Remoting

從微軟的產品角度來看,能夠說 .NET Remoting 就是 DCOM 的一種升級,它改善了不少功能,並極好的融合到 .NET 平臺下。Microsoft .NET Remoting 提供了一種容許對象經過應用程序域與另外一對象進行交互的框架。

.NET Remoting 提供了一種容許對象經過應用程序域與另外一對象進行交互的框架。這種框架提供了多種服務,包括激活和生存期支持,以及負責與遠程應用程序進行消息傳輸的通信通道。格式化程序用於在消息經過通道傳輸以前,對其進行編碼和解碼。應用程序能夠在注重性能的場合使用二進制編碼,在須要與其餘遠程處理框架進行交互的場合使用 XML 編碼。在從一個應用程序域向另外一個應用程序域傳輸消息時,全部的 XML 編碼都使用 SOAP 協議。出於安全性方面的考慮,遠程處理提供了大量掛鉤,使得在消息流經過通道進行傳輸以前,安全接收器可以訪問消息和序列化流

.NET Remoting 對象

有三類對象能夠配置爲 .NET Remoting 對象。您能夠根據應用程序的須要來選擇對象類型:

  • Single Call(單一調用對象): 
    Single Call 能且只能爲一個請求提供服務。在須要對象完成的工做量有限的狀況下 Single Call 很是有用。Single Call 對象一般不要求存儲狀態信息,而且不能在方法調用之間保留狀態信息。可是,Single Call對象能夠配置爲負載平衡模式。
  • Singleton Objects(單一元素對象): 
    Singleton Objects 能夠爲多個客戶端提供服務,所以能夠經過保存客戶端調用的狀態信息來實現數據共享。當客戶端之間須要明確地共享數據,而且不能忽略建立和維護對象的開銷時,這類對象很是有用。
  • Client-Activated Objects (CAO,客戶端激活的對象): 
    CAO 是服務器端的對象,將根據來自客戶端的請求激活這些對象。這種激活服務器對象的方法與傳統的 COM coclass 激活方法很是類似。當客戶端使用「new」運算符提交對服務器對象的請求時,會向遠程應用程序發送一個激活請求消息。隨後,服務器建立被請求類的實例,並向調用它的客戶端應用程序返回 ObjRef。而後,使用此 ObjRef 在客戶端上建立代理。客戶端的方法調用將在代理上執行。客戶端激活的對象能夠爲特定的客戶端(不能跨越不一樣的客戶端對象)保存方法調用之間的狀態信息。每一個「new」調用都會向服務器類型的獨立實例返回代理。

在 .NET Remoting 中,能夠經過如下方式在應用程序之間傳遞對象:

  • 做爲方法調用中的參數,例如:public int myRemoteMethod (MyRemoteObject myObj)
  • 方法調用的返回值,例如:public MyRemoteObject myRemoteMethod(String myString)
  • 訪問 .NET 組件的屬性或字段獲得的值,例如:myObj.myNestedObject

對於 Marshal By Value (MBV,按值封送)的對象,當它在應用程序之間傳遞時,將建立一個完整的副本。

對於 Marshal By Reference (MBR,按引用封送)的對象,當它在應用程序之間傳遞時,將建立該對象的引用。當對象引用 (ObjRef) 到達遠程應用程序後,將轉變成「代理」返回給原始對象。 
   
下面是一個簡單 .NET Remoting 服務器對象的代碼示例:

using System; using System.Runtime.Remoting; namespace myRemoteService { // Well Known Web Service object public class myRemoteObject : MarshalByRefObject { // Method myRemoteMethod public String myRemoteMethod(String s) { return "Hello World"; } } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

下面是客戶端代碼來訪問這個對象:

using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using myRemoteService; public class Client { public static int Main(string[] args) { ChannelServices.RegisterChannel(new HttpChannel()); // Create an instance of a myRemoteObject class myRemoteObject myObj = ( myRemoteObject)Activator.GetObject(typeof(myRemoteObject), "http://myHost:7021/host/myRemoteObject.soap"); myObj. myRemoteMethod ("Hello World"); return 0; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

租用生存期

對於那些具備在應用程序以外傳送的對象引用的對象,將建立一個租用。租用具備一個租用時間。若是租用時間爲 0,則租用過時,對象就斷開與 .NET Romoting 框架的鏈接。一旦 AppDomain 中的全部對象引用都被釋放,則下次垃圾回收時,該對象將被回收。租用控制了對象的生存期。

對象有默認的租用階段。當客戶端要在同一服務器對象中維護狀態時,能夠經過許多方法擴展租用階段,使對象繼續生存:

  • 服務器對象能夠將其租用時間設置爲無限,這樣 Remoting 在垃圾回收時就不會回收此對象。
  • 客戶端能夠調用 RemotingServices.GetLifetimeService 方法,從 AppDomain 的租用管理器獲取服務器對象的租用。而後,客戶端能夠經過 Lease 對象調用 Lease.Renew 方法以延長租用。
  • 客戶端可用 AppDomain 的租用管理器爲特定的租用註冊負責人。當 Remoting 對象的租用過時時,租用管理器將通知負責人提出續租的申請。
  • 若是設置了 ILease::RenewOnCallTime 屬性,則每次調用 Remoting 對象時,都會用 RenewOnCallTime 屬性指定的總時間更新租用。

集成 .NET Remoting 對象

.NET Remoting 對象能夠集成在:

  • 託管可執行項: 
    .NET Remoting 對象能夠集成在任何常規的 .NET EXE 或託管服務中。
  • .NET 組件服務: 
    .NET Remoting 對象能夠集成在 .NET 組件服務基礎結構中,以便利用各類 COM+ 服務,例如:事務、JIT 和對象池等。
  • IIS : .NET Remoting 對象能夠集成在 Internet Information Server (IIS) 中。默認狀況下,集成在 IIS 中的 Remoting 對象經過 HTTP 通道接收消息。要在 IIS 中集成 Remoting 對象,必須建立一個虛擬的根,並將 remoting.config 文件複製到其中。包含 Remoting 對象的可執行文件或 DLL 應放在 IIS 根指向的目錄下的 bin 目錄中。須要注意的是,IIS 根名稱應該與配置文件中指定的應用程序名稱相同。當第一個消息到達應用程序時,Remoting 配置文件會自動加載。使用這種方法,能夠提供 .NET Remoting 對象做爲 Web 服務。

下面是一個 Remoting.cfg 文件的例子:

<configuration> <system.runtime.remoting> <application name="RemotingHello"> <service> <wellknown mode="SingleCall" type="Hello.HelloService, Hello" objectUri="HelloService.soap" /> </service> <channels> <channel port="8000" type="System.Runtime.Remoting.Channels.Http.HttpChannel, System.Runtime.Remoting" /> </channels> </application> </system.runtime.remoting> </configuration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

通道服務 (System.Runtime.Remoting.Channels)

.NET 應用程序和 AppDomain 之間使用消息進行通訊。.NET 通道服務爲這一通訊過程提供了基礎傳輸機制。

.NET 框架提供了 HTTP 和 TCP 通道,可是第三方也能夠編寫並使用本身的通道。默認狀況下,HTTP 通道使用 SOAP 進行通訊,TCP 通道使用二進制有效負載。

經過使用能夠編寫到集成混合應用程序中的自定義通道,能夠插入通道服務(使用 IChannel)。

下面是一個加載通道服務的例子:

public class myRemotingObj { HttpChannel httpChannel; TcpChannel tcpChannel; public void myRemotingMethod() { httpChannel = new HttpChannel(); tcpChannel = new TcpChannel(); ChannelServices.RegisterChannel(httpChannel); // Register the HTTP Channel ChannelServices.RegisterChannel(tcpChannel); // Register the TCP Channel } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

序列化格式化程序 (System.Runtime.Serialization.Formatters)

.NET 序列化格式化程序對 .NET 應用程序和 AppDomain 之間的消息進行編碼和解碼。在 .NET 運行時中有兩個本地格式化程序,分別爲二進制 (System.Runtime.Serialization.Formatters.Binary) 和 SOAP (System.Runtime.Serialization.Formatters.Soap)。

經過實現 IRemotingFormatter 接口,並將其插入到上文介紹的通道中,能夠插入序列化格式化程序。這樣,您能夠靈活地選擇通道和格式化程序的組合方式,採用最適合應用程序的方案。

例如:您能夠採用 HTTP 通道和二進制格式化程序(串行化二進制數據),也能夠採用 TCP 通道和 SOAP 格式。

Remoting 上下文

上下文是一個包含共享公共運行時屬性的對象的範圍。某些上下文屬性的例子是與同步和線程緊密相關的。當 .NET 對象被激活時,運行時將檢查當前上下文是否一致,若是不一致,將建立新的上下文。多個對象能夠同時在一個上下文中運行,而且一個 AppDomain 中能夠有多個上下文。

一個上下文中的對象調用另外一個上下文中的對象時,調用將經過上下文代理來執行,而且會受組合的上下文屬性的強制策略影響。新對象的上下文一般是基於類的元數據屬性選擇的。

能夠與上下文綁定的類稱做上下文綁定類。上下文綁定類能夠具備稱爲上下文屬性的專用自定義屬性。上下文屬性是徹底可擴展的,您能夠建立這些屬性並將它們附加到本身的類中。與上下文綁定的對象是從 System.ContextBoundObject 導出的。

.NET Remoting 元數據和配置文件

元數據

.NET 框架使用元數據和程序集來存儲有關組件的信息,使得跨語言編程成爲可能。.NET Remoting 使用元數據來動態建立代理對象。在客戶端建立的代理對象具備與原始類相同的成員。可是,代理對象的實現僅僅將全部請求經過 .NET Remoting 運行時轉發給原始對象。序列化格式化程序使用元數據在方法調用和有效負載數據流之間來回轉換。

客戶端能夠經過如下方法獲取訪問 Remoting 對象所需的元數據信息:

  • 服務器對象的 .NET 程序集 : 服務器對象能夠建立元數據程序集,並將其分發給客戶端。在編譯客戶端對象時,客戶端對象能夠引用這些程序集。在客戶端和服務器都是託管組件的封閉環境中,這種方法很是有用。
  • Remoting 對象能夠提供 WSDL文件,用於說明對象及其方法。全部能夠根據 WSDL 文件讀取和生成 SOAP 請求的客戶端均可以調用此對象,或使用 SOAP 與之通訊。使用與 .NET SDK 一同分發的 SOAPSUDS.EXE 工具,.NET Remoting 服務器對象能夠生成具備元數據功能的 WSDL 文件。當某個組織但願提供全部客戶都能訪問和使用的公共服務時,這種方法很是有用。
  • .NET 客戶可使用 SOAPSUDS 工具從服務器上下載 XML 架構(在服務器上生成),生成僅包含元數據(沒有代碼)的源文件或程序集。您能夠根據須要將源文件編譯到客戶端應用程序中。若是多層應用程序中某一層的對象須要訪問其餘層的 Remoting 對象,則常用此方法。
配置文件

   
配置文件(.config 文件)用於指定給定對象的各類 Remoting 特有信息。一般狀況下,每一個 AppDomain 都有本身的配置文件。使用配置文件有助於實現位置的透明性。配置文件中指定的詳細信息也能夠經過編程來完成。使用配置文件的主要好處在於,它將與客戶端代碼無關的配置信息分離出來,這樣在往後更改時僅須要修改配置文件,而不用編輯和從新編譯源文件。.NET Remoting 的客戶端和服務器對象都使用配置文件。

典型的配置文件包含如下信息及其餘信息:

  • 集成應用程序信息
  • 對象名稱
  • 對象的 URI
  • 註冊的通道(能夠同時註冊多個通道)
  • 服務器對象的租用時間信息

下面是一個配置文件示例:

<configuration> <system.runtime.remoting> <application name="HelloNew"> <lifetime leaseTime="20ms" sponsorshipTimeout="20ms" renewOnCallTime="20ms" /> <client url="http://localhost:8000/RemotingHello"> <wellknown type="Hello.HelloService, MyHello" url="http://localhost:8000/RemotingHello/HelloService.soap" /> <activated type="Hello.AddService, MyHello"/> </client> <channels> <channel port="8001" type="System.Runtime.Remoting.Channels.Http.HttpChannel, System.Runtime.Remoting" /> </channels> </application> </system.runtime.remoting> </configuration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

.NET Remoting 方案

瞭解 .NET Remoting 如何工做以後,讓咱們來看一下各類方案,分析如何在不一樣的方案中充分發揮 .NET Remoting 的優點。下表列出了可能的客戶端/服務器組合,以及默認狀況下采用的底層協議和有效負載。請注意,.NET Remoting 框架是可擴展的,您能夠編寫本身的通訊通道和序列化格式化程序。

客戶端 服務器 有效負載 協議
.NET 組件 .NET 組件 SOAP/XML http
.NET 組件 .NET 組件 二進制 TCP
託管/非託管 .NET Web 服務 SOAP/XML http
.NET 組件 非託管的傳統 COM 組件 NDR(網絡數據表示形式) DCOM
非託管的傳統 COM 組件 .NET 組件 NDR DCOM
使用 HTTP-SOAP

Web 服務是能夠經過 URL 尋址的資源,並經過編程向須要使用這些資源的客戶端返回信息。客戶端使用 Web Services 時沒必要考慮其實現細節。Web Services 使用稱爲「合約」的嚴格定義的接口,此接口採用 Web 服務說明語言 (WSDL) 文件描述。

.NET Remoting 對象能夠集成在 IIS 中,做爲 Web 服務提供。任何可使用 WSDL 文件的客戶端均可以按照 WSDL 文件中指定的合約,對 Remoting 對象執行 SOAP 調用。IIS 使用 ISAPI 擴展將這些請求路由到相應的對象。這樣,Remoting 對象就能夠做爲 Web 服務對象來使用,從而充分發揮 .NET 框架基礎結構的做用。若是您但願不一樣平臺/環境的程序均可以訪問對象,能夠採用這種配置。這種配置便於客戶端經過防火牆訪問您的 .NET 對象。

圖10 .NET 使用 HTTP-SOAP

圖10 .NET 使用 HTTP-SOAP

使用 SOAP-HTTP 通道

默認狀況下,HTTP 通道使用 SOAP 格式化程序。所以,若是客戶端須要經過 Internet 訪問對象,可使用 HTTP 通道。由於這種方法使用 HTTP,因此此配置容許經過防火牆遠程訪問 .NET 對象。只須要按前一節中介紹的方法將這些對象集成在 IIS 中,便可將它配置爲 Web 服務對象。隨後,客戶端就能夠讀取這些對象的 WSDL 文件,使用 SOAP 與 Remoting 對象通訊。

使用 TCP 通道

默認狀況下,TCP 通道使用二進制格式化程序。此格式化程序以二進制格式對數據進行序列化,並使用原始 socket 在網絡中傳送數據。若是對象部署在受防火牆保護的封閉環境中,此方法是理想的選擇。這種方法使用 socket 在對象之間傳遞二進制數據,所以性能極佳。因爲它使用 TCP 通道來提供對象,所以在封閉環境中具備低開銷的優勢。因爲防火牆和配置的問題,此方法不宜在 Internet 上使用。

圖11 .NET 使用 TCP 通道

圖11 .NET 使用 TCP 通道

使用非託管的 COM 組件

能夠經過 COM Interop Service 調用非託管的傳統 COM 組件。當 .NET Remoting 客戶端對象建立 COM 對象的實例時,該對象經過運行時可調用包裝程序 (RCW) 來提供。其中,RCW 擔當真正的非託管對象的代理。對於 .NET 客戶,這些包裝程序看起來和 .NET 客戶端的任何其餘託管類同樣。但實際上,它們僅僅是託管 (.NET) 和非託管 (COM) 代碼之間的封送調用。

一樣地,您能夠將 .NET Remoting 服務器對象提供給傳統 COM 客戶端。當 COM 客戶端建立 .NET 對象的實例時,該對象經過 COM 可調用包裝程序 (CCW) 來提供。其中,CCW 擔當真正的託管對象的代理。 
   
這兩種方案都使用 DCOM 通訊。若是環境中既有傳統的 COM 組件,又有 .NET 組件,那麼這種互操做性將爲您提供便利。 
  

總結

Microsoft .NET 框架提供了強大、可擴展、獨立於語言的框架,適合開發可靠、可伸縮的分佈式系統。.NET Romoting 框架提供了根據系統需求進行遠程交互的強大手段。.NET Remoting 實現了與 Web 服務的無縫集成,並提供了一種方法,能夠提供 .NET 對象以供跨平臺訪問。

Java 中的 XML Web Services

Java RMI 與遠程對象進行交互,其實現是須要基於 Java 的模型。此外,它沒有使用 Web Services 和基於 HTTP 的消息傳遞。如今,已經出現了大量的軟件來支持基於 Java 的 Web Services。JAX-WS (Java API for XML Web Services) 就是做爲 Web Services 消息息和遠程過程調用的規範。它容許一個調用基於Java的web服務使用Java RMI(即。,相對透明的程序員)。JAX-WS 的一個目標是平臺互操做性。其 API 使用 SOAP 和WSDL。雙方不須要 Java 環境。

建立一個 RPC 端點

在服務器端,進行下面的步驟來建立一個 RPC 端點:

  • 定義一個接口(Java接口);
  • 實現服務;
  • 建立一個發佈者,用於建立服務的實例,併發佈一個服務名字。

在客戶端:

  • 建立一個代理(客戶端存根)。wsimport 命令根據 WSDL 文檔,建立一個客戶機存根;
  • 編寫一個客戶端,經過代理建立遠程服務的一個實例(存根),調用它的方法。

JAX-RPC 執行流程以下:

  • Java 客戶機調用存根上的方法(代理);
  • 存根調用適當的 Web 服務;
  • Web 服務器被調用並指導 JAX-WS 框架;
  • 框架調用實現;
  • 實現返回結果給該框架;
  • 該框架將結果返回給 Web 服務器;
  • 服務器將結果發送給客戶端存根;
  • 客戶端存根返回信息給調用者;

圖12 JAX-WS 調用流程

圖12 JAX-WS 調用流程

超越 SOAP

SOAP 雖然仍然是普遍部署應用,但在許多環境中不少廠商已經拋棄了 SOAP,轉而使用其餘更輕量、更容易理解、或者與 Web 交互模型更乾淨的機制。例如,Google 的 API 在2006年後就再也不支持 SOAP 接口,而是使用AJAX、XML-RPC 和 REST 做爲替代。一個匿名的微軟員工批評 SOAP 過於複雜,由於「咱們但願咱們的工具來閱讀它,而不是人」。無論上述言論是否準確,有一點是能夠確定的,SOAP 顯然是一個複雜和高度冗長的格式。

AJAX

Web 瀏覽器最初的設計,是爲 Web 頁面提供非動態的交互模型。Web 瀏覽器是創建在同步的請求-響應(request-response)的交互模型。發送一個請求到服務器,服務器返回整個頁面。在當時沒有更新部分頁面的好方法,而惟一可行的方法是利用幀,即將不一樣的頁面加載到每一幀,其實現是笨重的,也有很大的限制性。而改變了這一切的關鍵因素是:

  • 文檔對象模型(Document Object Model)和 JavaScript 的出現,使得能夠以編程方式來更改 Web 頁面的各個部分;
  • AJAX 提供了與服務器以非阻塞方式進行交互,即容許底層 JavaScript 在等待服務器結果時,用戶仍然能夠與頁面進行交互。

AJAX 全稱是 Asynchronous JavaScript And XML(異步的 JavaScript 和 XML)。讓咱們看看這些三項:

  • 它是異步的,由於客戶端等待服務器結果不會被阻塞;
  • AJAX 集成到了 JavaScript,做爲瀏覽器解釋 Web 頁面的一部分。JavaScript 使用 HTTPRequest 來調用 AJAX 請求。JavaScript 也可能修改文檔對象模型,定義了頁面的樣子;
  • 數據以 XML 文檔形式發送和接收。(在後期發展中,AJAX 也支持其餘的數據格式,好比 JSON)

AJAX 在推進 Web 2.0 的過程當中發揮了重要的,好比產生了不少高度交互的服務,如Google Maps、Writely等。基本上,它容許 JavaScript 發出HTTP 請求,獲取和處理結果,刷新局部頁面元素而不是整個頁面。在大多數瀏覽器請求的格式以下:

new XMLHttpRequest() xmlhttp.open(「HEAD」, 「index.html」, true)Tell object:
  • 1
  • 2

REST

SOAP 在建立本身的消息傳遞協議時是基於HTTP,但實際上 REST (REpresentational State Transfer) 的方式纔是保持 Web 的原理和使用 HTTP 協議的核心部分。

原始的 HTTP 協議已經定義了四個命令,清晰地映射到各類數據(定義爲「資源」)操做:

  • PUT (插入)
  • GET (選擇)
  • POST (更新)
  • DELETE (刪除)

REST 其背後的理念是使用這些 HTTP 命令來請求和操做數據。做爲 HTTP協議的一部分,REST 使用 URL 來引用對象和操做。考慮這個 HTTP 操做列表的例子:

HTTP GET //www.waylau.com/parts
  • 1

這個命令將返回一個 XML 文檔,其中包含部分的列表。注意,返回的不是一個網頁,只是一個包含所請求的數據 XML 數據結構。

<?xml version="1.0"?> <p:Parts xmlns:p="http://www.waylau.com" xmlns:xlink="http://www.w3.org/1999/xlink"> <Part id="00345" xlink:href="http://www.waylau.com/parts/00345"/> <Part id="00346" xlink:href="http://www.waylau.com/parts/00346"/> <Part id="00347" xlink:href="http://www.waylau.com/parts/00347"/> <Part id="00348" xlink:href="http://www.waylau.com/parts/00348"/> </p:Parts>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

要特定部分的詳細信息,發送一個HTTP get 命令:

HTTP GET //www.waylau.com/parts/00345
  • 1

這將返回一個特定的信息部分:

<?xml version="1.0"?> <p:Part xmlns:p="http://www.waylau.com" xmlns:xlink="http://www.w3.org/1999/xlink"> <Part-ID>00345</Part-ID> <Name>Widget-A</Name> <Description>This part is used within the frap assembly</Description> <Specification xlink:href="http://www.waylau.com/parts/00345/specification"/> <UnitCost currency="USD">0.10</UnitCost> <Quantity>10</Quantity> </p:Part>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

注意,上面例子簡化了 partid 做爲 URL 的參數。例如:

HTTP GET //www.waylau.com/parts?partid=00345
  • 1

REST 不是 RPC,但也有相似的請求-響應模式。制定透明度請求、封送數據、解析響應這些不屬於 REST。REST 應用很是普遍,如 Yahoo! Search API、Ruby on Rails、Twiter 和 Open Zing Services 等。

Google Protocol Buffers:封送處理

有些時候,不只僅是爲了 RPC 和 Web Services 的須要,程序員只是想簡化對網絡上的數據的封送編組和解封的操做。Google Protocol Buffers 就是爲序列化結構化數據提供了一種有效的機制,使它容易對網絡上的數據進行編碼和解碼。Protocol Buffers 是一個緊湊的二進制格式比 XML 更簡單、體積更小、速度更快。他們是獨立於語言的,只定義數據類型。每一個消息是對數據名稱、類型和值的結構化集合。消息結構定義在一個高級別的格式,相似於許多接口定義語言。而後文件能夠根據你選擇的語言來編譯轉換成與該語言相應的格式。Protocol Buffers 在 Google 中普遍使用。目前已經有超過48000種不一樣的消息類型定義。Protocol Buffers 能夠被運用在類 RPC 消息傳遞以及持久性存儲(將數據轉換成標準的串行形式寫入到一個文件中)。下面是一個定義 Protocol Buffers 的例子:

message Person {
  required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType {  MOBILE = 0;  HOME = 1;  WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

請注意,這隻定義了數據結構,而不是功能。使用這個結構的例子是:

Person person; person.set_name("John Doe"); person.set_id(1234); person.set_email("jdoe@example.com"); fstream output("myfile", ios::out | ios::binary); person.SerializeToOstream(&output);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

即便與緊湊的 XML 版本相比,Protocol Buffers 在時間和空間方面,解析將更加有效。下面是二者的對比.

這個是 XML 格式:

<person> <name>John Doe</name> <email>jdoe@example.com</email> </person>
  • 1
  • 2
  • 3
  • 4

這個沒有編譯的 Protocol Buffers 格式:

person { name: "John Doe" email: "jdoe@example.com" }
  • 1
  • 2
  • 3
  • 4

Protocol Buffers 產生的二進制消息大約是28字節長,解析耗時大概須要100-200ns。相比之下,XML 版本須要69個字節長(是 Protocol Buffers 的 2.5倍),耗時是5000-10000ns(是 Protocol Buffers 的 50倍)。

JSON

JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。它基於 ECMAScript 的一個子集。JSON 採用徹底獨立於語言的文本格式,可是也使用了相似於 C 語言家族的習慣(包括C、C++、C#、Java、JavaScript、Perl、Python 等)。這些特性使 JSON 成爲理想的數據交換語言。易於人閱讀和編寫,同時也易於機器解析和生成。JSON 不是一個諸如 Google Protocol Buffers 的二進制格式,所以適合使用基於 HTTP的消息傳遞。JSON 是能夠做爲 XML 替代品,在遠程過程調用中,不少語言都支持 JSON-RPC。記住,這只是一個消息傳遞格式,JSON 並無試圖提供 RPC 庫來支持服務發現、綁定、託管和垃圾收集。

參考引用

相關文章
相關標籤/搜索