很長時間以來都沒有怎麼好好搞清楚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
在說RPC和HTTP的區別以前,我覺的有必要了解一下OSI的七層網絡結構模型(雖然實際應用中基本上都是五層),它能夠分爲如下幾層: (從上到下)ios
實際應用過程當中,五層協議結構裏面是沒有表示層和會話層的。應該說它們和應用層合併了。咱們應該將重點放在應用層和傳輸層這兩個層面。由於HTTP是應用層協議,而TCP是傳輸層協議。好,知道了網絡的分層模型之後咱們能夠更好地理解爲何RPC服務相比HTTP服務要Nice一些!程序員
從三個角度來介紹RPC服務:分別是RPC架構,同步異步調用以及流行的RPC框架。web
先說說RPC服務的基本架構吧。容許我可恥地盜一幅圖哈~咱們能夠很清楚地看到,一個完整的RPC架構裏面包含了四個核心的組件,分別是Client ,Server,Client Stub以及Server Stub,這個Stub你們能夠理解爲存根。分別說說這幾個組件:ajax
RPC主要是用在大型企業裏面,由於大型企業裏面系統繁多,業務線複雜,並且效率優點很是重要的一塊,這個時候RPC的優點就比較明顯了。實際的開發當中是這麼作的,項目通常使用maven來管理。好比咱們有一個處理訂單的系統服務,先聲明它的全部的接口(這裏就是具體指Java中的interface
),而後將整個項目打包爲一個jar
包,服務端這邊引入這個二方庫,而後實現相應的功能,客戶端這邊也只須要引入這個二方庫便可調用了。爲何這麼作?主要是爲了減小客戶端這邊的jar
包大小,由於每一次打包發佈的時候,jar
包太多老是會影響效率。另外也是將客戶端和服務端解耦,提升代碼的可移植性。spring
什麼是同步調用?什麼是異步調用?同步調用
就是客戶端等待調用執行完成並返回結果。異步調用
就是客戶端不等待調用執行完成返回結果,不過依然能夠經過回調函數等接收到返回結果的通知。若是客戶端並不關心結果,則能夠變成一個單向的調用。這個過程有點相似於Java中的callable
和runnable
接口,咱們進行異步執行的時候,若是須要知道執行的結果,就可使用callable
接口,而且能夠經過Future
類獲取到異步執行的結果信息。若是不關心執行的結果,直接使用runnable
接口就能夠了,由於它不返回結果,固然啦,callable
也是能夠的,咱們不去獲取Future
就能夠了。編程
目前流行的開源RPC框架仍是比較多的。下面重點介紹三種:
偷偷告訴你
集團內部已經不怎麼使用dubbo啦,如今用的比較多的叫HSF,又名「好舒服」。後面有可能會開源,你們拭目以待。
其實在好久之前,我對於企業開發的模式一直定性爲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 是遠程過程調用(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傳遞的過程
注:上述論文,能夠在線閱讀 http://www.cs.virginia.edu/~zaher/classes/CS656/birrel.pdf。
遠程過程調用採用客戶機/服務器(C/S)模式。請求程序就是一個客戶機,而服務提供程序就是一臺服務器。和常規或本地過程調用同樣,遠程過程調用是同步操做,在遠程過程結果返回以前,須要暫時停止請求程序。使用相同地址空間的低權進程或低權線程容許同時運行多個遠程過程調用。
讓咱們看看本地過程調用是如何實現的。考慮下面的 C 語言的調用:
count = read(fd, buf, nbytes);
其中,fd 爲一個整型數,表示一個文件。buf 爲一個字符數組,用於存儲讀入的數據。 nbytes 爲另外一個整型數,用於記錄實際讀入的字節數。若是該調用位於主程序中,那麼在調用以前堆棧的狀態如圖2(a)所示。爲了進行調用,調用方首先把參數反序壓入堆棧,即爲最後一個參數先壓入,如圖2(b)所示。在 read 操做運行完畢後,它將返回值放在某個寄存器中,移出返回地址,並將控制權交回給調用方。調用方隨後將參數從堆棧中移出,使堆棧還原到最初的狀態。
圖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原理
當消息到達服務器時,服務器上的操做系統將它傳遞給服務器存根(server stub)。服務器存根是客戶存根在服務器端的等價物,也是一段代碼,用來將經過網絡輸入的請求轉換爲本地過程調用。服務器存根通常先調用 receive ,而後被阻塞,等待消息輸入。收到消息後,服務器將參數由消息中提取出來,而後以常規方式調用服務器上的相應過程(如圖3所示)。從服務器角度看,過程好像是由客戶直接調用的同樣:參數和返回地址都位於堆棧中,一切都很正常。服務器執行所要求的操做,隨後將獲得的結果以常規的方式返回給調用方。以 read 爲例,服務器將用數據填充 read 中第二個參數指向的緩衝區,該緩存區是屬於服務器存根內部的。
調用完後,服務器存根要將控制權教會給客戶發出調用的過程,它將結果(緩衝區)打包成消息,隨後調用 send 將結果返回給客戶。過後,服務器存根通常會再次調用 receive,等待下一個輸入的請求。
客戶機器接收到消息後,客戶操做系統發現該消息屬於某個客戶進程(實際上該進程是客戶存根,知識操做系統沒法區分兩者)。操做系統將消息複製到相應的緩存區中,隨後解除對客戶進程的阻塞。客戶存根檢查該消息,將結果提取出來並複製給調用者,然後以一般的方式返回。當調用者在 read 調用進行完畢後從新得到控制權時,它所知道的惟一事就是已經獲得了所需的數據。它不指導操做是在本地操做系統進行,仍是遠程完成。
整個方法,客戶方能夠簡單地忽略不關心的內容。客戶所涉及的操做只是執行普通的(本地)過程調用來訪問遠程服務,它並不須要直接調用 send 和 receive 。消息傳遞的全部細節都隱藏在雙方的庫過程當中,就像傳統庫隱藏了執行實際系統調用的細節同樣。
概況來講,遠程過程調用包含以下步驟:
以上步驟就是將客戶過程對客戶存根發出的本地調用轉換成對服務器過程的本地調用,而客戶端和服務器都不會意識到中間步驟的存在。
RPC 的主要好處是雙重的。首先,程序員可使用過程調用語義來調用遠程函數並獲取響應。其次,簡化了編寫分佈式應用程序的難度,由於 RPC 隱藏了全部的網絡代碼存根函數。應用程序沒必要擔憂一些細節,好比 socket、端口號以及數據的轉換和解析。在 OSI 參考模型,RPC 跨越了會話層和表示層。
要實現遠程過程調用,需考慮如下幾個問題。
傳遞值參數比較簡單,下圖圖展現了一個簡單 RPC 進行遠程計算的例子。其中,遠程過程 add(i,j) 有兩個參數 i 和 j, 其結果是返回 i 和 j 的算術和。
圖4 經過RPC進行遠程計算的步驟
經過 RPC 進行遠程計算的步驟有:
固然,這裏只是作了簡單的演示,在實際分佈式系統中,還須要考慮其餘狀況,由於不一樣的機器對於數字、字符和其餘類型的數據項的表示方式常有差別。好比整數型,就有 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 實現支持幾個,並容許用戶選擇。
相比於本地過程調用,遠程過程調用出錯的機會將會更多。因爲本地過程調用沒有過程調用失敗的概念,項目使用遠程過程調用必須準備測試遠程過程調用的失敗或捕獲異常。
調用一個普通的過程語義很簡單:當咱們調用時,過程被執行。遠程過程徹底一次性調用成功是很是難以實現。執行遠程過程能夠有以下結果:
RPC 系統一般會提供至少一次或最多一次的語義,或者在二者之間選擇。若是須要了解應用程序的性質和遠程過程的功能是否安全,能夠經過屢次調用同一個函數來驗證。若是一個函數能夠運行任何次數而不影響結果,這是冪等(idempotent)函數的,如天天的時間、數學函數、讀取靜態數據等。不然,它是一個非冪等(nonidempotent)函數,如添加或修改一個文件)。
毫無疑問,一個遠程過程調用將會比常規的本地過程調用慢得多,由於產生了額外的步驟以及網絡傳輸自己存在延遲。然而,這並不該該阻止咱們使用遠程過程調用。
使用 RPC,咱們必須關注各類安全問題:
遠程過程調用有諸多的優勢:
任何 RPC 實現都須要提供一組支持庫。這些包括:
因此,判斷一種通訊方式是不是 RPC,就看它是否提供上述的 API。
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.x 裏的編譯數據。以後,客戶端和服務器端函數可能被編譯,並連接各自 rpcgen 生成的存根函數。
在舊版本里,傳輸協議只能將字符串「tcp」或字符串「udp」來指定各自的 IP 服務 RPC,且僅限於 Linux 實現的 RPC。爲了使接口更加靈活,UNIX 系統從版本 4 (SunOS 從版本 5)開始網絡選擇程序容許一個更普通的規範。他們搜索文件(/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 中的函數查找
當咱們開始客戶端程序時,它首先用遠程系統的名稱、程序編號、版本號和協議來調用 clnt_create 。它接觸遠程系統上的端口映射器,來爲系統找到合適的端口。
而後客戶端調用 RPC 存根函數(在本例中爲 bin_date_1)。該函數發送一條消息(如,數據報)到服務器(使用早些時候發現的端口號)並等待響應。對於數據報服務來講,若沒有接收到響應,它將從新發送一個固定的次數請求。
消息接着被遠程系統接收到,它調用服務器函數(bin_date_1)並將返回值返回給客戶端存根。客戶端存根然後返回到客戶端發出調用的代碼。
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 中的函數查找
面向對象的語言開始在1980年代末興起,很明顯,當時的 Sun ONC 和 DCE RPC 系統都沒有提供任何支持諸如從遠程類實例化遠程對象、跟蹤對象的實例或提供支持多態性。現有的 RPC 機制雖然能夠運做,但他們仍然不支持自動、透明的方式的面向對象編程技術。
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 經過。
雖然 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 體系的主要內容包括如下幾部分:
當客戶端發出請求時,ORB 作了以下事情:
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); }; };
IDL數據類型包括:
編程中最多見的實現方式是經過對象引用來實現請求。下面是一個使用 IDL 的例子:
Student st = ... // get object reference try { StudentInfo sinfo = st.getinfo("Fred Grampp"); } catch (Throwable e) { ... // error }
在 CORBA 規範中,沒有明確說明不一樣廠商的中間件產品要實現全部的服務功能,而且容許廠商開發本身的服務類型。所以, 不一樣廠商的 ORB 產品對 CORBA 服務的支持能力不一樣,使咱們在針對待開發系統的功能進行中間件產品選擇時,有更多的選擇餘地。
CORBA 的不足有:
更多有關 CORBA 的優缺點,能夠參閱 Michi Henning 的《The rise and fall of CORBA》。
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 的設計目標應該是:
分佈式對象模型與本地 Java 對象模型類似點在於:
不一樣點在於:
全部的遠程接口都繼承自 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; }
注意,每一個方法必須在 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);
圖7 Java 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 程序得到返回值。
要完成以上步驟須要有如下幾個步驟:
圖8 Java RMI 架構
根據 Java 虛擬機的垃圾回收機制原理,在分佈式環境下,服務器進程須要知道哪些對象再也不由客戶端引用,從而能夠被刪除(垃圾回收)。在 JVM中,Java 使用引用計數。當引用計數歸零時,對象將會垃圾回收。在RMI,Java 支持兩種操做:dirty 和 clean。本地 JVM 按期發送一個 dirty 到服務器來講明該對象仍在使用。按期重發 dirty 的週期是由服務器租賃時間來決定的。當客戶端沒有須要更多的本地引用遠程對象時,它發送一個 clean 調用給服務器。不像 DCOM,服務器不須要計算每一個客戶機使用的對象,只是簡單的作下通知。若是它租賃時間到期以前沒有接收到任何 dirty 或者 clean 的消息,則能夠安排將對象刪除。
因爲互聯網的興起,Web 瀏覽器成爲占主導地位的用於訪問信息的模型。如今的應用設計的首要任務大多數是提供用戶經過瀏覽器來訪問,而不是編程訪問或操做數據。
網頁設計關注的是內容。解析展示方面每每是繁瑣的。傳統 RPC 解決方案能夠工做在互聯網上,但問題是,他們一般嚴重依賴於動態端口分配,每每要進行額外的防火牆配置。
Web Services 成爲一組協議,容許服務被髮布、發現,並用於技術無關的形式。即服務不該該依賴於客戶的語言、操做系統或機器架構。
Web Services 的實現通常是使用 Web 服務器做爲服務請求的管道。客戶端訪問該服務,首先是經過一個 HTTP 協議發送請求到服務器上的 Web 服務器。Web 服務器配置識別 URL 的一部分路徑名或文件名後綴並將請求傳遞給特定的瀏覽器插件模塊。這個模塊能夠除去頭、解析數據(若是須要),並根據須要調用其餘函數或模塊。對於這個實現流,一個常見的例子是瀏覽器對於 Java Servlet 的支持。HTTP 請求會被轉發到 JVM 運行的服務端代碼來執行處理。
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>
這個例子中,方法 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(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>
其中<soap:Envelope>
是 SOAP 消息中的根節點,是 SOAP 消息中必須的部分。<soap:Header>
是 SOAP 消息中可選部分,是指消息頭。<soap:Body>
是 SOAP 中必須部分,是指消息體。
上面例子的 SOAP 消息結構以下:
圖9 SOAP 消息結構
SOAP 它只是提供了一個標準化的消息結構,爲了實現它每每須要用 WSDL 來描述 Web Services 的方法。WSDL (Web Services Description Language) 是基於 XML 的一種用於描述 Web Services 以及如何訪問 Web Services 的語言。
WSDL 文檔包括如下幾個部分:
下面是一個 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>
從微軟的產品角度來看,能夠說 .NET Remoting 就是 DCOM 的一種升級,它改善了不少功能,並極好的融合到 .NET 平臺下。Microsoft .NET Remoting 提供了一種容許對象經過應用程序域與另外一對象進行交互的框架。
.NET Remoting 提供了一種容許對象經過應用程序域與另外一對象進行交互的框架。這種框架提供了多種服務,包括激活和生存期支持,以及負責與遠程應用程序進行消息傳輸的通信通道。格式化程序用於在消息經過通道傳輸以前,對其進行編碼和解碼。應用程序能夠在注重性能的場合使用二進制編碼,在須要與其餘遠程處理框架進行交互的場合使用 XML 編碼。在從一個應用程序域向另外一個應用程序域傳輸消息時,全部的 XML 編碼都使用 SOAP 協議。出於安全性方面的考慮,遠程處理提供了大量掛鉤,使得在消息流經過通道進行傳輸以前,安全接收器可以訪問消息和序列化流
有三類對象能夠配置爲 .NET Remoting 對象。您能夠根據應用程序的須要來選擇對象類型:
在 .NET Remoting 中,能夠經過如下方式在應用程序之間傳遞對象:
public int myRemoteMethod (MyRemoteObject myObj)
public MyRemoteObject myRemoteMethod(String myString)
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"; } } }
下面是客戶端代碼來訪問這個對象:
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; } }
對於那些具備在應用程序以外傳送的對象引用的對象,將建立一個租用。租用具備一個租用時間。若是租用時間爲 0,則租用過時,對象就斷開與 .NET Romoting 框架的鏈接。一旦 AppDomain 中的全部對象引用都被釋放,則下次垃圾回收時,該對象將被回收。租用控制了對象的生存期。
對象有默認的租用階段。當客戶端要在同一服務器對象中維護狀態時,能夠經過許多方法擴展租用階段,使對象繼續生存:
RemotingServices.GetLifetimeService
方法,從 AppDomain 的租用管理器獲取服務器對象的租用。而後,客戶端能夠經過 Lease 對象調用 Lease.Renew 方法以延長租用。ILease::RenewOnCallTime
屬性,則每次調用 Remoting 對象時,都會用 RenewOnCallTime 屬性指定的總時間更新租用。.NET Remoting 對象能夠集成在:
下面是一個 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>
.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 } }
.NET 序列化格式化程序對 .NET 應用程序和 AppDomain 之間的消息進行編碼和解碼。在 .NET 運行時中有兩個本地格式化程序,分別爲二進制 (System.Runtime.Serialization.Formatters.Binary
) 和 SOAP (System.Runtime.Serialization.Formatters.Soap
)。
經過實現 IRemotingFormatter 接口,並將其插入到上文介紹的通道中,能夠插入序列化格式化程序。這樣,您能夠靈活地選擇通道和格式化程序的組合方式,採用最適合應用程序的方案。
例如:您能夠採用 HTTP 通道和二進制格式化程序(串行化二進制數據),也能夠採用 TCP 通道和 SOAP 格式。
上下文是一個包含共享公共運行時屬性的對象的範圍。某些上下文屬性的例子是與同步和線程緊密相關的。當 .NET 對象被激活時,運行時將檢查當前上下文是否一致,若是不一致,將建立新的上下文。多個對象能夠同時在一個上下文中運行,而且一個 AppDomain 中能夠有多個上下文。
一個上下文中的對象調用另外一個上下文中的對象時,調用將經過上下文代理來執行,而且會受組合的上下文屬性的強制策略影響。新對象的上下文一般是基於類的元數據屬性選擇的。
能夠與上下文綁定的類稱做上下文綁定類。上下文綁定類能夠具備稱爲上下文屬性的專用自定義屬性。上下文屬性是徹底可擴展的,您能夠建立這些屬性並將它們附加到本身的類中。與上下文綁定的對象是從 System.ContextBoundObject
導出的。
.NET 框架使用元數據和程序集來存儲有關組件的信息,使得跨語言編程成爲可能。.NET Remoting 使用元數據來動態建立代理對象。在客戶端建立的代理對象具備與原始類相同的成員。可是,代理對象的實現僅僅將全部請求經過 .NET Remoting 運行時轉發給原始對象。序列化格式化程序使用元數據在方法調用和有效負載數據流之間來回轉換。
客戶端能夠經過如下方法獲取訪問 Remoting 對象所需的元數據信息:
配置文件(.config 文件)用於指定給定對象的各類 Remoting 特有信息。一般狀況下,每一個 AppDomain 都有本身的配置文件。使用配置文件有助於實現位置的透明性。配置文件中指定的詳細信息也能夠經過編程來完成。使用配置文件的主要好處在於,它將與客戶端代碼無關的配置信息分離出來,這樣在往後更改時僅須要修改配置文件,而不用編輯和從新編譯源文件。.NET Remoting 的客戶端和服務器對象都使用配置文件。
典型的配置文件包含如下信息及其餘信息:
下面是一個配置文件示例:
<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>
瞭解 .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 |
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
默認狀況下,HTTP 通道使用 SOAP 格式化程序。所以,若是客戶端須要經過 Internet 訪問對象,可使用 HTTP 通道。由於這種方法使用 HTTP,因此此配置容許經過防火牆遠程訪問 .NET 對象。只須要按前一節中介紹的方法將這些對象集成在 IIS 中,便可將它配置爲 Web 服務對象。隨後,客戶端就能夠讀取這些對象的 WSDL 文件,使用 SOAP 與 Remoting 對象通訊。
默認狀況下,TCP 通道使用二進制格式化程序。此格式化程序以二進制格式對數據進行序列化,並使用原始 socket 在網絡中傳送數據。若是對象部署在受防火牆保護的封閉環境中,此方法是理想的選擇。這種方法使用 socket 在對象之間傳遞二進制數據,所以性能極佳。因爲它使用 TCP 通道來提供對象,所以在封閉環境中具備低開銷的優勢。因爲防火牆和配置的問題,此方法不宜在 Internet 上使用。
圖11 .NET 使用 TCP 通道
能夠經過 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 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 端點:
在客戶端:
JAX-RPC 執行流程以下:
圖12 JAX-WS 調用流程
SOAP 雖然仍然是普遍部署應用,但在許多環境中不少廠商已經拋棄了 SOAP,轉而使用其餘更輕量、更容易理解、或者與 Web 交互模型更乾淨的機制。例如,Google 的 API 在2006年後就再也不支持 SOAP 接口,而是使用AJAX、XML-RPC 和 REST 做爲替代。一個匿名的微軟員工批評 SOAP 過於複雜,由於「咱們但願咱們的工具來閱讀它,而不是人」。無論上述言論是否準確,有一點是能夠確定的,SOAP 顯然是一個複雜和高度冗長的格式。
Web 瀏覽器最初的設計,是爲 Web 頁面提供非動態的交互模型。Web 瀏覽器是創建在同步的請求-響應(request-response)的交互模型。發送一個請求到服務器,服務器返回整個頁面。在當時沒有更新部分頁面的好方法,而惟一可行的方法是利用幀,即將不一樣的頁面加載到每一幀,其實現是笨重的,也有很大的限制性。而改變了這一切的關鍵因素是:
AJAX 全稱是 Asynchronous JavaScript And XML(異步的 JavaScript 和 XML)。讓咱們看看這些三項:
AJAX 在推進 Web 2.0 的過程當中發揮了重要的,好比產生了不少高度交互的服務,如Google Maps、Writely等。基本上,它容許 JavaScript 發出HTTP 請求,獲取和處理結果,刷新局部頁面元素而不是整個頁面。在大多數瀏覽器請求的格式以下:
new XMLHttpRequest() xmlhttp.open(「HEAD」, 「index.html」, true)Tell object:
SOAP 在建立本身的消息傳遞協議時是基於HTTP,但實際上 REST (REpresentational State Transfer) 的方式纔是保持 Web 的原理和使用 HTTP 協議的核心部分。
原始的 HTTP 協議已經定義了四個命令,清晰地映射到各類數據(定義爲「資源」)操做:
REST 其背後的理念是使用這些 HTTP 命令來請求和操做數據。做爲 HTTP協議的一部分,REST 使用 URL 來引用對象和操做。考慮這個 HTTP 操做列表的例子:
HTTP GET //www.waylau.com/parts
這個命令將返回一個 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>
要特定部分的詳細信息,發送一個HTTP get 命令:
HTTP GET //www.waylau.com/parts/00345
這將返回一個特定的信息部分:
<?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>
注意,上面例子簡化了 partid 做爲 URL 的參數。例如:
HTTP GET //www.waylau.com/parts?partid=00345
REST 不是 RPC,但也有相似的請求-響應模式。制定透明度請求、封送數據、解析響應這些不屬於 REST。REST 應用很是普遍,如 Yahoo! Search API、Ruby on Rails、Twiter 和 Open Zing Services 等。
有些時候,不只僅是爲了 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; }
請注意,這隻定義了數據結構,而不是功能。使用這個結構的例子是:
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);
即便與緊湊的 XML 版本相比,Protocol Buffers 在時間和空間方面,解析將更加有效。下面是二者的對比.
這個是 XML 格式:
<person> <name>John Doe</name> <email>jdoe@example.com</email> </person>
這個沒有編譯的 Protocol Buffers 格式:
person { name: "John Doe" email: "jdoe@example.com" }
Protocol Buffers 產生的二進制消息大約是28字節長,解析耗時大概須要100-200ns。相比之下,XML 版本須要69個字節長(是 Protocol Buffers 的 2.5倍),耗時是5000-10000ns(是 Protocol Buffers 的 50倍)。
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 庫來支持服務發現、綁定、託管和垃圾收集。