日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

RPC(Remote Procedure Calls)远程过程调用

發(fā)布時(shí)間:2023/12/31 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 RPC(Remote Procedure Calls)远程过程调用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

很長時(shí)間以來都沒有怎么好好搞清楚RPC(即Remote Procedure Call,遠(yuǎn)程過程調(diào)用)和HTTP調(diào)用的區(qū)別,不都是寫一個(gè)服務(wù)然后在客戶端調(diào)用么?這里請(qǐng)?jiān)试S我迷之一笑~Naive!本文簡單地介紹一下兩種形式的C/S架構(gòu),先說一下他們最本質(zhì)的區(qū)別,就是RPC主要是基于TCP/IP協(xié)議的,而HTTP服務(wù)主要是基于HTTP協(xié)議的,我們都知道HTTP協(xié)議是在傳輸層協(xié)議TCP之上的,所以效率來看的話,RPC當(dāng)然是要更勝一籌啦!下面來具體說一說RPC服務(wù)和HTTP服務(wù)。

為什么RPC呢?就是無法在一個(gè)進(jìn)程內(nèi),甚至一個(gè)計(jì)算機(jī)內(nèi)通過本地調(diào)用的方式完成的需求,比如比如不同的系統(tǒng)間的通訊,甚至不同的組織間的通訊。由于計(jì)算能力需要橫向擴(kuò)展,需要在多臺(tái)機(jī)器組成的集群上部署應(yīng)用。
RPC的協(xié)議有很多,比如最早的CORBA,Java RMI,Web Service的RPC風(fēng)格,Hessian,Thrift,甚至Rest API。

關(guān)于RPC
RPC框架,首先了解什么叫RPC,為什么要RPC,RPC是指遠(yuǎn)程過程調(diào)用,也就是說兩臺(tái)服務(wù)器A,B,一個(gè)應(yīng)用部署在A服務(wù)器上,想要調(diào)用B服務(wù)器上應(yīng)用提供的函數(shù)/方法,由于不在一個(gè)內(nèi)存空間,不能直接調(diào)用,需要通過網(wǎng)絡(luò)來表達(dá)調(diào)用的語義和傳達(dá)調(diào)用的數(shù)據(jù)。

?


比如說,一個(gè)方法可能是這樣定義的:
Employee getEmployeeByName(String fullName)
那么:

    • 首先,要解決通訊的問題,主要是通過在客戶端和服務(wù)器之間建立TCP連接,遠(yuǎn)程過程調(diào)用的所有交換的數(shù)據(jù)都在這個(gè)連接里傳輸。連接可以是按需連接,調(diào)用結(jié)束后就斷掉,也可以是長連接,多個(gè)遠(yuǎn)程過程調(diào)用共享同一個(gè)連接。
    • 第二,要解決尋址的問題,也就是說,A服務(wù)器上的應(yīng)用怎么告訴底層的RPC框架,如何連接到B服務(wù)器(如主機(jī)或IP地址)以及特定的端口,方法的名稱名稱是什么,這樣才能完成調(diào)用。比如基于Web服務(wù)協(xié)議棧的RPC,就要提供一個(gè)endpoint URI,或者是從UDDI服務(wù)上查找。如果是RMI調(diào)用的話,還需要一個(gè)RMI Registry來注冊(cè)服務(wù)的地址。
    • 第三,當(dāng)A服務(wù)器上的應(yīng)用發(fā)起遠(yuǎn)程過程調(diào)用時(shí),方法的參數(shù)需要通過底層的網(wǎng)絡(luò)協(xié)議如TCP傳遞到B服務(wù)器,由于網(wǎng)絡(luò)協(xié)議是基于二進(jìn)制的,內(nèi)存中的參數(shù)的值要序列化成二進(jìn)制的形式,也就是序列化(Serialize)或編組(marshal),通過尋址和傳輸將序列化的二進(jìn)制發(fā)送給B服務(wù)器。
    • 第四,B服務(wù)器收到請(qǐng)求后,需要對(duì)參數(shù)進(jìn)行反序列化(序列化的逆操作),恢復(fù)為內(nèi)存中的表達(dá)方式,然后找到對(duì)應(yīng)的方法(尋址的一部分)進(jìn)行本地調(diào)用,然后得到返回值。
    • 第五,返回值還要發(fā)送回服務(wù)器A上的應(yīng)用,也要經(jīng)過序列化的方式發(fā)送,服務(wù)器A接到后,再反序列化,恢復(fù)為內(nèi)存中的表達(dá)方式,交給A服務(wù)器上的應(yīng)用

OSI網(wǎng)絡(luò)七層模型

在說RPC和HTTP的區(qū)別之前,我覺的有必要了解一下OSI的七層網(wǎng)絡(luò)結(jié)構(gòu)模型(雖然實(shí)際應(yīng)用中基本上都是五層),它可以分為以下幾層: (從上到下)

  • 第一層:應(yīng)用層。定義了用于在網(wǎng)絡(luò)中進(jìn)行通信和傳輸數(shù)據(jù)的接口;
  • 第二層:表示層。定義不同的系統(tǒng)中數(shù)據(jù)的傳輸格式,編碼和解碼規(guī)范等;
  • 第三層:會(huì)話層。管理用戶的會(huì)話,控制用戶間邏輯連接的建立和中斷;
  • 第四層:傳輸層。管理著網(wǎng)絡(luò)中的端到端的數(shù)據(jù)傳輸;
  • 第五層:網(wǎng)絡(luò)層。定義網(wǎng)絡(luò)設(shè)備間如何傳輸數(shù)據(jù);
  • 第六層:鏈路層。將上面的網(wǎng)絡(luò)層的數(shù)據(jù)包封裝成數(shù)據(jù)幀,便于物理層傳輸;
  • 第七層:物理層。這一層主要就是傳輸這些二進(jìn)制數(shù)據(jù)。

實(shí)際應(yīng)用過程中,五層協(xié)議結(jié)構(gòu)里面是沒有表示層和會(huì)話層的。應(yīng)該說它們和應(yīng)用層合并了。我們應(yīng)該將重點(diǎn)放在應(yīng)用層和傳輸層這兩個(gè)層面。因?yàn)镠TTP是應(yīng)用層協(xié)議,而TCP是傳輸層協(xié)議。好,知道了網(wǎng)絡(luò)的分層模型以后我們可以更好地理解為什么RPC服務(wù)相比HTTP服務(wù)要Nice一些!

RPC服務(wù)

從三個(gè)角度來介紹RPC服務(wù):分別是RPC架構(gòu),同步異步調(diào)用以及流行的RPC框架。

RPC架構(gòu)

先說說RPC服務(wù)的基本架構(gòu)吧。允許我可恥地盜一幅圖哈~我們可以很清楚地看到,一個(gè)完整的RPC架構(gòu)里面包含了四個(gè)核心的組件,分別是Client?,Server,Client Stub以及Server Stub,這個(gè)Stub大家可以理解為存根。分別說說這幾個(gè)組件:

  • 客戶端(Client),服務(wù)的調(diào)用方。
  • 服務(wù)端(Server),真正的服務(wù)提供者。
  • 客戶端存根,存放服務(wù)端的地址消息,再將客戶端的請(qǐng)求參數(shù)打包成網(wǎng)絡(luò)消息,然后通過網(wǎng)絡(luò)遠(yuǎn)程發(fā)送給服務(wù)方。
  • 服務(wù)端存根,接收客戶端發(fā)送過來的消息,將消息解包,并調(diào)用本地的方法。

RPC主要是用在大型企業(yè)里面,因?yàn)榇笮推髽I(yè)里面系統(tǒng)繁多,業(yè)務(wù)線復(fù)雜,而且效率優(yōu)勢非常重要的一塊,這個(gè)時(shí)候RPC的優(yōu)勢就比較明顯了。實(shí)際的開發(fā)當(dāng)中是這么做的,項(xiàng)目一般使用maven來管理。比如我們有一個(gè)處理訂單的系統(tǒng)服務(wù),先聲明它的所有的接口(這里就是具體指Java中的interface),然后將整個(gè)項(xiàng)目打包為一個(gè)jar包,服務(wù)端這邊引入這個(gè)二方庫,然后實(shí)現(xiàn)相應(yīng)的功能,客戶端這邊也只需要引入這個(gè)二方庫即可調(diào)用了。為什么這么做?主要是為了減少客戶端這邊的jar包大小,因?yàn)槊恳淮未虬l(fā)布的時(shí)候,jar包太多總是會(huì)影響效率。另外也是將客戶端和服務(wù)端解耦,提高代碼的可移植性。

同步調(diào)用與異步調(diào)用

什么是同步調(diào)用?什么是異步調(diào)用?同步調(diào)用就是客戶端等待調(diào)用執(zhí)行完成并返回結(jié)果。異步調(diào)用就是客戶端不等待調(diào)用執(zhí)行完成返回結(jié)果,不過依然可以通過回調(diào)函數(shù)等接收到返回結(jié)果的通知。如果客戶端并不關(guān)心結(jié)果,則可以變成一個(gè)單向的調(diào)用。這個(gè)過程有點(diǎn)類似于Java中的callable和runnable接口,我們進(jìn)行異步執(zhí)行的時(shí)候,如果需要知道執(zhí)行的結(jié)果,就可以使用callable接口,并且可以通過Future類獲取到異步執(zhí)行的結(jié)果信息。如果不關(guān)心執(zhí)行的結(jié)果,直接使用runnable接口就可以了,因?yàn)樗环祷亟Y(jié)果,當(dāng)然啦,callable也是可以的,我們不去獲取Future就可以了。

流行的RPC框架

目前流行的開源RPC框架還是比較多的。下面重點(diǎn)介紹三種:

  • gRPC是Google最近公布的開源軟件,基于最新的HTTP2.0協(xié)議,并支持常見的眾多編程語言。 我們知道HTTP2.0是基于二進(jìn)制的HTTP協(xié)議升級(jí)版本,目前各大瀏覽器都在快馬加鞭的加以支持。 這個(gè)RPC框架是基于HTTP協(xié)議實(shí)現(xiàn)的,底層使用到了Netty框架的支持。
  • Thrift是Facebook的一個(gè)開源項(xiàng)目,主要是一個(gè)跨語言的服務(wù)開發(fā)框架。它有一個(gè)代碼生成器來對(duì)它所定義的IDL定義文件自動(dòng)生成服務(wù)代碼框架。用戶只要在其之前進(jìn)行二次開發(fā)就行,對(duì)于底層的RPC通訊等都是透明的。不過這個(gè)對(duì)于用戶來說的話需要學(xué)習(xí)特定領(lǐng)域語言這個(gè)特性,還是有一定成本的。
  • Dubbo是阿里集團(tuán)開源的一個(gè)極為出名的RPC框架,在很多互聯(lián)網(wǎng)公司和企業(yè)應(yīng)用中廣泛使用。協(xié)議和序列化框架都可以插拔是及其鮮明的特色。同樣 的遠(yuǎn)程接口是基于Java Interface,并且依托于spring框架方便開發(fā)。可以方便的打包成單一文件,獨(dú)立進(jìn)程運(yùn)行,和現(xiàn)在的微服務(wù)概念一致。
  • 偷偷告訴你集團(tuán)內(nèi)部已經(jīng)不怎么使用dubbo啦,現(xiàn)在用的比較多的叫HSF,又名“好舒服”。后面有可能會(huì)開源,大家拭目以待。

    HTTP服務(wù)

    其實(shí)在很久以前,我對(duì)于企業(yè)開發(fā)的模式一直定性為HTTP接口開發(fā),也就是我們常說的RESTful風(fēng)格的服務(wù)接口。的確,對(duì)于在接口不多、系統(tǒng)與系統(tǒng)交互較少的情況下,解決信息孤島初期常使用的一種通信手段;優(yōu)點(diǎn)就是簡單、直接、開發(fā)方便。利用現(xiàn)成的http協(xié)議進(jìn)行傳輸。我們記得之前本科實(shí)習(xí)在公司做后臺(tái)開發(fā)的時(shí)候,主要就是進(jìn)行接口的開發(fā),還要寫一大份接口文檔,嚴(yán)格地標(biāo)明輸入輸出是什么?說清楚每一個(gè)接口的請(qǐng)求方法,以及請(qǐng)求參數(shù)需要注意的事項(xiàng)等。比如下面這個(gè)例子:
    POST http://www.httpexample.com/restful/buyer/info/share
    接口可能返回一個(gè)JSON字符串或者是XML文檔。然后客戶端再去處理這個(gè)返回的信息,從而可以比較快速地進(jìn)行開發(fā)。但是對(duì)于大型企業(yè)來說,內(nèi)部子系統(tǒng)較多、接口非常多的情況下,RPC框架的好處就顯示出來了,首先就是長鏈接,不必每次通信都要像http一樣去3次握手什么的,減少了網(wǎng)絡(luò)開銷;其次就是RPC框架一般都有注冊(cè)中心,有豐富的監(jiān)控管理;發(fā)布、下線接口、動(dòng)態(tài)擴(kuò)展等,對(duì)調(diào)用方來說是無感知、統(tǒng)一化的操作。

    ?

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

    本文介紹了什么是遠(yuǎn)程過程調(diào)用(RPC),RPC 有哪些常用的方法,RPC 經(jīng)歷了哪些發(fā)展階段,以及比較了各種 RPC 技術(shù)的優(yōu)劣。

    什么是 RPC

    RPC 是遠(yuǎn)程過程調(diào)用(Remote Procedure Call)的縮寫形式,Birrell 和 Nelson 在 1984 發(fā)表于 ACM Transactions on Computer Systems 的論文《Implementing remote procedure calls》對(duì) RPC 做了經(jīng)典的詮釋。RPC 是指計(jì)算機(jī) A 上的進(jìn)程,調(diào)用另外一臺(tái)計(jì)算機(jī) B 上的進(jìn)程,其中 A 上的調(diào)用進(jìn)程被掛起,而 B 上的被調(diào)用進(jìn)程開始執(zhí)行,當(dāng)值返回給 A 時(shí),A 進(jìn)程繼續(xù)執(zhí)行。調(diào)用方可以通過使用參數(shù)將信息傳送給被調(diào)用方,而后可以通過傳回的結(jié)果得到信息。而這一過程,對(duì)于開發(fā)人員來說是透明的。

    圖1 描述了數(shù)據(jù)報(bào)在一個(gè)簡單的RPC傳遞的過程

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

    遠(yuǎn)程過程調(diào)用采用客戶機(jī)/服務(wù)器(C/S)模式。請(qǐng)求程序就是一個(gè)客戶機(jī),而服務(wù)提供程序就是一臺(tái)服務(wù)器。和常規(guī)或本地過程調(diào)用一樣,遠(yuǎn)程過程調(diào)用是同步操作,在遠(yuǎn)程過程結(jié)果返回之前,需要暫時(shí)中止請(qǐng)求程序。使用相同地址空間的低權(quán)進(jìn)程或低權(quán)線程允許同時(shí)運(yùn)行多個(gè)遠(yuǎn)程過程調(diào)用。

    RPC 的基本操作

    讓我們看看本地過程調(diào)用是如何實(shí)現(xiàn)的。考慮下面的 C 語言的調(diào)用:

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

    其中,fd 為一個(gè)整型數(shù),表示一個(gè)文件。buf 為一個(gè)字符數(shù)組,用于存儲(chǔ)讀入的數(shù)據(jù)。 nbytes 為另一個(gè)整型數(shù),用于記錄實(shí)際讀入的字節(jié)數(shù)。如果該調(diào)用位于主程序中,那么在調(diào)用之前堆棧的狀態(tài)如圖2(a)所示。為了進(jìn)行調(diào)用,調(diào)用方首先把參數(shù)反序壓入堆棧,即為最后一個(gè)參數(shù)先壓入,如圖2(b)所示。在 read 操作運(yùn)行完畢后,它將返回值放在某個(gè)寄存器中,移出返回地址,并將控制權(quán)交回給調(diào)用方。調(diào)用方隨后將參數(shù)從堆棧中移出,使堆棧還原到最初的狀態(tài)。

    圖2 過程調(diào)用中的參數(shù)傳遞

    RPC 背后的思想是盡量使遠(yuǎn)程過程調(diào)用具有與本地調(diào)用相同的形式。假設(shè)程序需要從某個(gè)文件讀取數(shù)據(jù),程序員在代碼中執(zhí)行 read 調(diào)用來取得數(shù)據(jù)。在傳統(tǒng)的系統(tǒng)中, read 例程由鏈接器從庫中提取出來,然后鏈接器再將它插入目標(biāo)程序中。 read 過程是一個(gè)短過程,一般通過執(zhí)行一個(gè)等效的 read 系統(tǒng)調(diào)用來實(shí)現(xiàn)。即,read 過程是一個(gè)位于用戶代碼與本地操作系統(tǒng)之間的接口。

    雖然 read 中執(zhí)行了系統(tǒng)調(diào)用,但它本身依然是通過將參數(shù)壓入堆棧的常規(guī)方式調(diào)用的。如圖2(b)所示,程序員并不知道 read 干了啥。

    RPC 是通過類似的途徑來獲得透明性。當(dāng) read 實(shí)際上是一個(gè)遠(yuǎn)程過程時(shí)(比如在文件服務(wù)器所在的機(jī)器上運(yùn)行的過程),庫中就放入 read 的另外一個(gè)版本,稱為客戶存根(client stub)。這種版本的 read 過程同樣遵循圖2(b)的調(diào)用次序,這點(diǎn)與原來的 read 過程相同。另一個(gè)相同點(diǎn)是其中也執(zhí)行了本地操作系統(tǒng)調(diào)用。唯一不同點(diǎn)是它不要求操作系統(tǒng)提供數(shù)據(jù),而是將參數(shù)打包成消息,而后請(qǐng)求此消息發(fā)送到服務(wù)器,如圖3所示。在對(duì) send 的調(diào)用后,客戶存根調(diào)用 receive 過程,隨即阻塞自己,直到收到響應(yīng)消息。

    圖3 客戶與服務(wù)器之間的RPC原理

    當(dāng)消息到達(dá)服務(wù)器時(shí),服務(wù)器上的操作系統(tǒng)將它傳遞給服務(wù)器存根(server stub)。服務(wù)器存根是客戶存根在服務(wù)器端的等價(jià)物,也是一段代碼,用來將通過網(wǎng)絡(luò)輸入的請(qǐng)求轉(zhuǎn)換為本地過程調(diào)用。服務(wù)器存根一般先調(diào)用 receive ,然后被阻塞,等待消息輸入。收到消息后,服務(wù)器將參數(shù)由消息中提取出來,然后以常規(guī)方式調(diào)用服務(wù)器上的相應(yīng)過程(如圖3所示)。從服務(wù)器角度看,過程好像是由客戶直接調(diào)用的一樣:參數(shù)和返回地址都位于堆棧中,一切都很正常。服務(wù)器執(zhí)行所要求的操作,隨后將得到的結(jié)果以常規(guī)的方式返回給調(diào)用方。以 read 為例,服務(wù)器將用數(shù)據(jù)填充 read 中第二個(gè)參數(shù)指向的緩沖區(qū),該緩存區(qū)是屬于服務(wù)器存根內(nèi)部的。

    調(diào)用完后,服務(wù)器存根要將控制權(quán)教會(huì)給客戶發(fā)出調(diào)用的過程,它將結(jié)果(緩沖區(qū))打包成消息,隨后調(diào)用 send 將結(jié)果返回給客戶。事后,服務(wù)器存根一般會(huì)再次調(diào)用 receive,等待下一個(gè)輸入的請(qǐng)求。

    客戶機(jī)器接收到消息后,客戶操作系統(tǒng)發(fā)現(xiàn)該消息屬于某個(gè)客戶進(jìn)程(實(shí)際上該進(jìn)程是客戶存根,知識(shí)操作系統(tǒng)無法區(qū)分二者)。操作系統(tǒng)將消息復(fù)制到相應(yīng)的緩存區(qū)中,隨后解除對(duì)客戶進(jìn)程的阻塞。客戶存根檢查該消息,將結(jié)果提取出來并復(fù)制給調(diào)用者,而后以通常的方式返回。當(dāng)調(diào)用者在 read 調(diào)用進(jìn)行完畢后重新獲得控制權(quán)時(shí),它所知道的唯一事就是已經(jīng)得到了所需的數(shù)據(jù)。它不指導(dǎo)操作是在本地操作系統(tǒng)進(jìn)行,還是遠(yuǎn)程完成。

    整個(gè)方法,客戶方可以簡單地忽略不關(guān)心的內(nèi)容。客戶所涉及的操作只是執(zhí)行普通的(本地)過程調(diào)用來訪問遠(yuǎn)程服務(wù),它并不需要直接調(diào)用 send 和 receive 。消息傳遞的所有細(xì)節(jié)都隱藏在雙方的庫過程中,就像傳統(tǒng)庫隱藏了執(zhí)行實(shí)際系統(tǒng)調(diào)用的細(xì)節(jié)一樣。

    概況來說,遠(yuǎn)程過程調(diào)用包含如下步驟:

  • 客戶過程以正常的方式調(diào)用客戶存根;
  • 客戶存根生成一個(gè)消息,然后調(diào)用本地操作系統(tǒng);
  • 客戶端操作系統(tǒng)將消息發(fā)送給遠(yuǎn)程操作系統(tǒng);
  • 遠(yuǎn)程操作系統(tǒng)將消息交給服務(wù)器存根;
  • 服務(wù)器存根調(diào)將參數(shù)提取出來,而后調(diào)用服務(wù)器;
  • 服務(wù)器執(zhí)行要求的操作,操作完成后將結(jié)果返回給服務(wù)器存根;
  • 服務(wù)器存根將結(jié)果打包成一個(gè)消息,而后調(diào)用本地操作系統(tǒng);
  • 服務(wù)器操作系統(tǒng)將含有結(jié)果的消息發(fā)送給客戶端操作系統(tǒng);
  • 客戶端操作系統(tǒng)將消息交給客戶存根;
  • 客戶存根將結(jié)果從消息中提取出來,返回給調(diào)用它的客戶存根。
  • 以上步驟就是將客戶過程對(duì)客戶存根發(fā)出的本地調(diào)用轉(zhuǎn)換成對(duì)服務(wù)器過程的本地調(diào)用,而客戶端和服務(wù)器都不會(huì)意識(shí)到中間步驟的存在。

    RPC 的主要好處是雙重的。首先,程序員可以使用過程調(diào)用語義來調(diào)用遠(yuǎn)程函數(shù)并獲取響應(yīng)。其次,簡化了編寫分布式應(yīng)用程序的難度,因?yàn)?RPC 隱藏了所有的網(wǎng)絡(luò)代碼存根函數(shù)。應(yīng)用程序不必?fù)?dān)心一些細(xì)節(jié),比如 socket、端口號(hào)以及數(shù)據(jù)的轉(zhuǎn)換和解析。在 OSI 參考模型,RPC 跨越了會(huì)話層和表示層。

    實(shí)現(xiàn)遠(yuǎn)程過程調(diào)用

    要實(shí)現(xiàn)遠(yuǎn)程過程調(diào)用,需考慮以下幾個(gè)問題。

    如何傳遞參數(shù)

    傳遞值參數(shù)

    傳遞值參數(shù)比較簡單,下圖圖展示了一個(gè)簡單 RPC 進(jìn)行遠(yuǎn)程計(jì)算的例子。其中,遠(yuǎn)程過程 add(i,j) 有兩個(gè)參數(shù) i 和 j, 其結(jié)果是返回 i 和 j 的算術(shù)和。

    圖4 通過RPC進(jìn)行遠(yuǎn)程計(jì)算的步驟

    通過 RPC 進(jìn)行遠(yuǎn)程計(jì)算的步驟有:

  • 將參數(shù)放入消息中,并在消息中添加要調(diào)用的過程的名稱或者編碼。
  • 消息到達(dá)服務(wù)器后,服務(wù)器存根堆該消息進(jìn)行分析,以判明需要調(diào)用哪個(gè)過程,隨后執(zhí)行相應(yīng)的調(diào)用。
  • 服務(wù)器運(yùn)行完畢后,服務(wù)器存根將服務(wù)器得到的結(jié)果打包成消息送回客戶存根,客戶存根將結(jié)果從消息中提取出來,把結(jié)果值返回給客戶端。
  • 當(dāng)然,這里只是做了簡單的演示,在實(shí)際分布式系統(tǒng)中,還需要考慮其他情況,因?yàn)椴煌臋C(jī)器對(duì)于數(shù)字、字符和其他類型的數(shù)據(jù)項(xiàng)的表示方式常有差異。比如整數(shù)型,就有 Big Endian 和 Little Endian 之分。

    傳遞引用參數(shù)

    傳遞引用參數(shù)相對(duì)來說比較困難。單純傳遞參數(shù)的引用(也包含指針)是完全沒有意義的,因?yàn)橐玫刂穫鬟f給遠(yuǎn)程計(jì)算機(jī),其指向的內(nèi)存位置可能跟遠(yuǎn)程系統(tǒng)上完全不同。如果你想支持傳遞引用參數(shù),你必須發(fā)送參數(shù)的副本,將它們放置在遠(yuǎn)程系統(tǒng)內(nèi)存中,向他們傳遞一個(gè)指向服務(wù)器函數(shù)的指針,然后將對(duì)象發(fā)送回客戶端,復(fù)制它的引用。如果遠(yuǎn)程過程調(diào)用必須支持引用復(fù)雜的結(jié)構(gòu),比如樹和鏈表,他們需要將結(jié)構(gòu)復(fù)制到一個(gè)無指針的表示里面(比如,一個(gè)扁平的樹),并傳輸?shù)皆谶h(yuǎn)程端來重建數(shù)據(jù)結(jié)構(gòu)。

    如何表示數(shù)據(jù)

    在本地系統(tǒng)上不存在數(shù)據(jù)不相容的問題,因?yàn)閿?shù)據(jù)格式總是相同的。而在分布式系統(tǒng)中,不同遠(yuǎn)程機(jī)器上可能有不同的字節(jié)順序,不同大小的整數(shù),以及不同的浮點(diǎn)表示。對(duì)于 RPC,如果想與異構(gòu)系統(tǒng)通信,我們就需要想出一個(gè)“標(biāo)準(zhǔn)”來對(duì)所有數(shù)據(jù)類型進(jìn)行編碼,并可以作為參數(shù)傳遞。例如,ONC RPC 使用 XDR (eXternal Data Representation) 格式 。這些數(shù)據(jù)表示格式可以使用隱式或顯式類型。隱式類型,是指只傳遞值,而不傳遞變量的名稱或類型。常見的例子是 ONC RPC 的 XDR 和 DCE RPC 的 NDR。顯式類型,指需要傳遞每個(gè)字段的類型以及值。常見的例子是 ISO 標(biāo)準(zhǔn) ASN.1 (Abstract Syntax Notation)、JSON (JavaScript Object Notation)、Google Protocol Buffers、以及各種基于 XML 的數(shù)據(jù)表示格式。

    如何選用傳輸協(xié)議

    有些實(shí)現(xiàn)只允許使用一個(gè)協(xié)議(例如 TCP )。大多數(shù) RPC 實(shí)現(xiàn)支持幾個(gè),并允許用戶選擇。

    出錯(cuò)時(shí),會(huì)發(fā)生什么

    相比于本地過程調(diào)用,遠(yuǎn)程過程調(diào)用出錯(cuò)的機(jī)會(huì)將會(huì)更多。由于本地過程調(diào)用沒有過程調(diào)用失敗的概念,項(xiàng)目使用遠(yuǎn)程過程調(diào)用必須準(zhǔn)備測試遠(yuǎn)程過程調(diào)用的失敗或捕獲異常。

    遠(yuǎn)程調(diào)用的語義是什么

    調(diào)用一個(gè)普通的過程語義很簡單:當(dāng)我們調(diào)用時(shí),過程被執(zhí)行。遠(yuǎn)程過程完全一次性調(diào)用成功是非常難以實(shí)現(xiàn)。執(zhí)行遠(yuǎn)程過程可以有如下結(jié)果:

    • 如果服務(wù)器崩潰或進(jìn)程在運(yùn)行服務(wù)器代碼之前就死了,那么遠(yuǎn)程過程會(huì)被執(zhí)行0次;
    • 如果一切工作正常,遠(yuǎn)程過程會(huì)被執(zhí)行1次;
    • 如果服務(wù)器返回服務(wù)器存根后在發(fā)送響應(yīng)前就奔潰了,遠(yuǎn)程過程會(huì)被執(zhí)行1次或者多次。客戶端接收不到返回的響應(yīng),可以決定再試一次,因此出現(xiàn)多次執(zhí)行函數(shù)。如果沒有再試一次,函數(shù)執(zhí)行一次;
    • 如果客戶機(jī)超時(shí)和重新傳輸,那么遠(yuǎn)程過程會(huì)被執(zhí)行多次。也有可能是原始請(qǐng)求延遲了。兩者都可能會(huì)執(zhí)行或不執(zhí)行。

    RPC 系統(tǒng)通常會(huì)提供至少一次或最多一次的語義,或者在兩者之間選擇。如果需要了解應(yīng)用程序的性質(zhì)和遠(yuǎn)程過程的功能是否安全,可以通過多次調(diào)用同一個(gè)函數(shù)來驗(yàn)證。如果一個(gè)函數(shù)可以運(yùn)行任何次數(shù)而不影響結(jié)果,這是冪等(idempotent)函數(shù)的,如每天的時(shí)間、數(shù)學(xué)函數(shù)、讀取靜態(tài)數(shù)據(jù)等。否則,它是一個(gè)非冪等(nonidempotent)函數(shù),如添加或修改一個(gè)文件)。

    遠(yuǎn)程調(diào)用的性能怎么樣

    毫無疑問,一個(gè)遠(yuǎn)程過程調(diào)用將會(huì)比常規(guī)的本地過程調(diào)用慢得多,因?yàn)楫a(chǎn)生了額外的步驟以及網(wǎng)絡(luò)傳輸本身存在延遲。然而,這并不應(yīng)該阻止我們使用遠(yuǎn)程過程調(diào)用。

    遠(yuǎn)程調(diào)用安全嗎?

    使用 RPC,我們必須關(guān)注各種安全問題:

    • 客戶端發(fā)送消息到遠(yuǎn)程過程,那個(gè)過程是可信的嗎?
    • 客戶端發(fā)送消息到遠(yuǎn)程計(jì)算機(jī),那個(gè)遠(yuǎn)程機(jī)器是可信的嗎?
    • 服務(wù)器如何驗(yàn)證接收的消息是來自合法的客戶端嗎?服務(wù)器如何識(shí)別客戶端?
    • 消息在網(wǎng)絡(luò)中傳播如何防止時(shí)被其他進(jìn)程嗅探?
    • 可以由其他進(jìn)程消息被攔截和修改時(shí)遍歷網(wǎng)絡(luò)從客戶端到服務(wù)器或服務(wù)器端?
    • 協(xié)議能防止重播攻擊嗎?
    • 如何防止消息在網(wǎng)絡(luò)傳播中被意外損壞或截?cái)?

    遠(yuǎn)程過程調(diào)用的優(yōu)點(diǎn)

    遠(yuǎn)程過程調(diào)用有諸多的優(yōu)點(diǎn):

    • 你不必?fù)?dān)心傳輸?shù)刂穯栴}。服務(wù)器可以綁定到任何可用的端口,然后用 RPC 名稱服務(wù)來注冊(cè)端口。客戶端將通過該名稱服務(wù)來找到對(duì)應(yīng)的端口號(hào)所需要的程序。而這一切對(duì)于程序員來說是透明的。
    • 系統(tǒng)可以獨(dú)立于傳輸提供者。自動(dòng)生成服務(wù)器存根使其可以在系統(tǒng)上的任何一個(gè)傳輸提供者上可用,包括 TCP 和 UDP,而這些,客戶端可以動(dòng)態(tài)選擇的。當(dāng)代碼發(fā)送以后,接收消息是自動(dòng)生成的,而不需要額外的編程代碼。
    • 應(yīng)用程序在客戶端只需要知道一個(gè)傳輸?shù)刂贰Q服務(wù),負(fù)責(zé)告訴應(yīng)用程序去哪里連接服務(wù)器函數(shù)集。
    • 使用函數(shù)調(diào)用模型來代替 socket 的發(fā)送/接收(讀/寫)接口。用戶不需要處理參數(shù)的解析。

    RPC API

    任何 RPC 實(shí)現(xiàn)都需要提供一組支持庫。這些包括:

    • 名稱服務(wù)操作:?
      注冊(cè)和查找綁定信息(端口、機(jī)器)。允許一個(gè)應(yīng)用程序使用動(dòng)態(tài)端口(操作系統(tǒng)分配的);
    • 綁定操作:使用適當(dāng)?shù)膮f(xié)議建立客戶機(jī)/服務(wù)器通信(建立通信端點(diǎn));
    • 終端操作:注冊(cè)端點(diǎn)信息(協(xié)議、端口號(hào)、機(jī)器名)到名稱服務(wù)并監(jiān)聽過程調(diào)用請(qǐng)求。這些函數(shù)通常被自動(dòng)生成的主程序——服務(wù)器存根(骨架)所調(diào)用;
    • 安全操作:系統(tǒng)應(yīng)該提供機(jī)制保證客戶端和服務(wù)器之間能夠相互驗(yàn)證,兩者之間提供一個(gè)安全的通信通道;
    • 國際化操作(可能):這是很少的一部分 RPC 包可能包括了轉(zhuǎn)換時(shí)間格式、貨幣格式和特定于語言的在字符串表的字符串的功能;
    • 封送處理/數(shù)據(jù)轉(zhuǎn)換操作:函數(shù)將數(shù)據(jù)序列化為一個(gè)普通的的字節(jié)數(shù)組,通過網(wǎng)絡(luò)進(jìn)行傳遞,并能夠重建;
    • 存根內(nèi)存管理和垃圾收集:存根可能需要分配內(nèi)存來存儲(chǔ)參數(shù),特別是模擬引用傳遞語義。RPC 包需要分配和清理任何這樣的分配。他們也可能需要為創(chuàng)建網(wǎng)絡(luò)緩沖區(qū)而分配內(nèi)存。RPC 包支持對(duì)象,RPC 系統(tǒng)需要一種跟蹤遠(yuǎn)程客戶端是否仍有引用對(duì)象或一個(gè)對(duì)象是否可以刪除。
    • 程序標(biāo)識(shí)操作:允許應(yīng)用程序訪問(或處理) RPC 接口集的標(biāo)識(shí)符,這樣的服務(wù)器提供的接口集可以被用來交流和使用。
    • 對(duì)象和函數(shù)的標(biāo)識(shí)操作:?
      允許將遠(yuǎn)程函數(shù)或遠(yuǎn)程對(duì)象的引用傳遞給其他進(jìn)程。并不是所有的 RPC 系統(tǒng)都支持。

    所以,判斷一種通信方式是否是 RPC,就看它是否提供上述的 API。

    第一代 RPC

    ONC RPC(以前稱為 Sun RPC)

    Sun 公司是第一個(gè)提供商業(yè)化 RPC 庫和 RPC 編譯器。在1980年代中期 Sun 計(jì)算機(jī)提供 RPC,并在 Sun Network File System(NFS) 得到支持。該協(xié)議被主要以 Sun 和 AT&T 為首的 Open Network Computing (開放網(wǎng)絡(luò)計(jì)算)作為一個(gè)標(biāo)準(zhǔn)來推動(dòng)。這是一個(gè)非常輕量級(jí) RPC 系統(tǒng)可用在大多數(shù) POSIX 和類 POSIX 操作系統(tǒng)中使用,包括 Linux、SunOS、OS X 和各種發(fā)布版本的 BSD。這樣的系統(tǒng)被稱為 Sun RPC 或 ONC RPC。

    ONC RPC 提供了一個(gè)編譯器,需要一個(gè)遠(yuǎn)程過程接口的定義來生成客戶機(jī)和服務(wù)器的存根函數(shù)。這個(gè)編譯器叫做 rpcgen。在運(yùn)行此編譯器之前,程序員必須提供接口定義。包含函數(shù)聲明的接口定義,通過版本號(hào)進(jìn)行分組,并被一個(gè)獨(dú)特的程序編碼來標(biāo)識(shí)。該程序編碼能夠讓客戶來確定所需的接口。版本號(hào)是非常有用的,即使客戶沒有更新到最新的代碼仍然可以連接到一個(gè)新的服務(wù)器,只要該服務(wù)器還支持舊接口。

    參數(shù)通過網(wǎng)絡(luò)轉(zhuǎn)化成一種隱式類型序列化格式被稱為 XDR (eXternal Data Representation)。這將確保參數(shù)能夠發(fā)送到異構(gòu)系統(tǒng)可以被正常使用,及時(shí)這些系統(tǒng)可能使用了不同的字節(jié)順序,不同大小的整數(shù),或不同的浮點(diǎn)或字符串表示。最后,Sun RPC 提供了一個(gè)實(shí)現(xiàn)必要的支持 RPC 協(xié)議和 socket 例程的運(yùn)行時(shí)庫。

    所有的程序員都需要寫是一個(gè)客戶端程序(client.c),服務(wù)器功能(server.c)和 RPC 接口定義(date.x)。當(dāng) RPC 接口定義(后綴為.x 的文件,例如 date.x)是用 rpcgen 編譯的,會(huì)創(chuàng)建三個(gè)或四個(gè)文件。下面是 date.x 的例子:

    • date.h:包含項(xiàng)目的定義、版本和聲明的函數(shù)。客戶端和服務(wù)器端功能應(yīng)該包括這個(gè)文件。
    • date_svc.c :C 語言代碼來實(shí)現(xiàn)服務(wù)器存根。
    • date_clnt.c :C 語言代碼來實(shí)現(xiàn)客戶端存根。
    • date_xdr.c :包含 XDR 例程來將數(shù)據(jù)轉(zhuǎn)化為 XDR 格式。如果這個(gè)文件生成,它應(yīng)該編譯并用來鏈接在客戶端和服務(wù)器的函數(shù)。

    創(chuàng)建客戶端和服務(wù)器可執(zhí)行文件的第一步是定義在文件?date.x?里的編譯數(shù)據(jù)。之后,客戶端和服務(wù)器端函數(shù)可能被編譯,并鏈接各自 rpcgen 生成的存根函數(shù)。

    在舊版本里,傳輸協(xié)議只能將字符串“tcp”或字符串“udp”來指定各自的 IP 服務(wù) RPC,且僅限于 Linux 實(shí)現(xiàn)的 RPC。為了使接口更加靈活,UNIX 系統(tǒng)從版本 4 (SunOS 從版本 5)開始網(wǎng)絡(luò)選擇程序允許一個(gè)更普通的規(guī)范。他們搜索文件(/etc/netconfig),來查找第一個(gè)滿足您需求的提供者。最后一個(gè)參數(shù)可以是:

    • “netpath”:搜索 NETPATH 環(huán)境變量用于首選傳輸提供者的序列;
    • “circuit_n”:找到第一個(gè)在 NETPATH 列表中的虛擬電路提供者;
    • “datagram_n”:找到第一個(gè) NETPATH 列表中的數(shù)據(jù)報(bào)提供者;
    • “visible”:找到第一個(gè)在 /etc/netconfig 的可見傳輸提供者;
    • “circuit_v”:找到第一個(gè)在 /etc/netconfig 的可見虛擬電路傳輸提供者;
    • “datagram_v”:找到第一個(gè)在 /etc/netconfig 的可見數(shù)據(jù)報(bào)傳輸提供者;

    每個(gè)遠(yuǎn)程過程調(diào)用最初僅限于接受一個(gè)輸入?yún)?shù)。系統(tǒng)只是后來修改為支持多個(gè)參數(shù)。支持單一參數(shù) RPC 在一些 rpcgen 的版本中仍然是默認(rèn)的,比如蘋果的 OS X。傳遞多個(gè)參數(shù)必須通過定義一個(gè)結(jié)構(gòu),包含所需的參數(shù),初始化它,并傳遞這個(gè)結(jié)構(gòu)。

    遠(yuǎn)程過程調(diào)用返回一個(gè)指針指向結(jié)果而不是期望的結(jié)果。服務(wù)器函數(shù)必須修改來能接受一個(gè) RPC 定義(.x 文件)中聲明的值的指針作為輸入,并返回一個(gè)結(jié)果值的指針。在服務(wù)器上,一個(gè)指針必須是指向靜態(tài)數(shù)據(jù)的指針。否則,當(dāng)過程返回或釋放過程的框架所指出的區(qū)域?qū)⑽炊x。在客戶端,返回指針可以讓我們區(qū)分一個(gè)失敗的 RPC(空指針)和一個(gè)空返回從服務(wù)器(間接空指針)。

    RPC 過程的名稱若在 RPC 定義文件中做了定義,則會(huì)轉(zhuǎn)換為小寫,并在后綴價(jià)下劃線,后跟一個(gè)版本號(hào)。例如,BIN_DATE 轉(zhuǎn)成為引用函數(shù) bin_date_1 。您的服務(wù)器必須實(shí)現(xiàn) bin_date_1。

    當(dāng)我們運(yùn)行這個(gè)程序時(shí),會(huì)發(fā)生什么?

    服務(wù)器

    當(dāng)我們啟動(dòng)服務(wù)器,服務(wù)器存根代碼(程序)在后臺(tái)運(yùn)行運(yùn)行。它創(chuàng)建一個(gè) socket 并可綁定任何本地端口到 socket。然后調(diào)用一個(gè)在 RPC 庫的函數(shù) svc_register,來注冊(cè)程序編號(hào)和版本。這個(gè)是用來聯(lián)系 port mapper(端口映射器)。port mapper 是一個(gè)獨(dú)立的進(jìn)程,通常是在系統(tǒng)啟動(dòng)時(shí)啟動(dòng)。它跟蹤端口號(hào)、版本號(hào)以及程序編號(hào)。在 UNIX 系統(tǒng)版本4中,這個(gè)進(jìn)程稱為 rpcbind。在 Linux 、OS X 和 BSD 系統(tǒng),它被稱為 portmap。

    圖5 ONC RPC 中的函數(shù)查找

    客戶端

    當(dāng)我們開始客戶端程序時(shí),它首先用遠(yuǎn)程系統(tǒng)的名稱、程序編號(hào)、版本號(hào)和協(xié)議來調(diào)用 clnt_create 。它接觸遠(yuǎn)程系統(tǒng)上的端口映射器,來為系統(tǒng)找到合適的端口。

    然后客戶端調(diào)用 RPC 存根函數(shù)(在本例中為 bin_date_1)。該函數(shù)發(fā)送一條消息(如,數(shù)據(jù)報(bào))到服務(wù)器(使用早些時(shí)候發(fā)現(xiàn)的端口號(hào))并等待響應(yīng)。對(duì)于數(shù)據(jù)報(bào)服務(wù)來說,若沒有接收到響應(yīng),它將重新發(fā)送一個(gè)固定的次數(shù)請(qǐng)求。

    消息接著被遠(yuǎn)程系統(tǒng)接收到,它調(diào)用服務(wù)器函數(shù)(bin_date_1)并將返回值返回給客戶端存根。客戶端存根而后返回到客戶端發(fā)出調(diào)用的代碼。

    分布式計(jì)算環(huán)境中的 RPC(DCE RPC)

    DCE(Distributed Computing Environment,分布式計(jì)算環(huán)境)是一組由OFS(Open Software Foundation,開放軟件基金會(huì))設(shè)計(jì)的組件,用來提供支持分布式應(yīng)用和分布式環(huán)境。與 X/Open 合并后,這組織成為了 The Open Group (開放式開發(fā)組)。DCE 提供的組件包括一個(gè)分布式文件服務(wù)、時(shí)間服務(wù)、目錄服務(wù)以及其他服務(wù)。當(dāng)然,我們感興趣的是 DCE 的遠(yuǎn)程過程調(diào)用。它非常類似于 Sun RPC。接口是由 Interface Definition Notation (IDN) 定義的。類似于 Sun RPC,接口定義就像函數(shù)原型。

    Sun RPC 不足之處在于,服務(wù)器的標(biāo)識(shí)是一個(gè)“獨(dú)特”的 32-bit 數(shù)字。雖然這是一個(gè)比在 socket 中 16-bit 可用空間更大的空間,但仍然無法滿足數(shù)字唯一性的需求。DCE RPC 考慮到了這一缺陷,它無需程序員來處理編碼。在編寫應(yīng)用程序時(shí)的第一步是從 uuidgen 程序獲得一個(gè)惟一的 ID。這個(gè)程序會(huì)生成一個(gè)包含 ID 接口的原型 IDN 文件,并保證永遠(yuǎn)不會(huì)再次使用。它是一個(gè) 128-bit 的值,其中包含一個(gè)位置代碼和創(chuàng)建時(shí)間的編碼。然后用戶編輯原型文件,填寫遠(yuǎn)程過程聲明。

    在這一步后,IDN 的編譯器 dceidl(類似于 rpcgen)會(huì)生成一個(gè)頭、客戶機(jī)存根和服務(wù)器存根。

    Sun RPC 的另一個(gè)缺陷是,客戶端必須知道服務(wù)器在哪臺(tái)機(jī)器上。當(dāng)它要訪問時(shí),必須要詢問機(jī)器上的 RPC 名稱服務(wù)程序編碼所對(duì)應(yīng)的端口號(hào)。DCE 支持將多個(gè)機(jī)器組織成為管理實(shí)體,稱為 cells。cell 目錄服務(wù)器使得每臺(tái)機(jī)器知道如何與另外一臺(tái)負(fù)責(zé)維護(hù) cell 信息服務(wù)機(jī)器交互。

    在 Sun RPC 中,服務(wù)器只能用本地名稱服務(wù)(端口映射器)來注冊(cè)其程序編號(hào)到端口映射。而在 DCE 中,服務(wù)器用 RPC 守護(hù)進(jìn)程(名稱服務(wù)器)來注冊(cè)其端點(diǎn)(端口)到本地機(jī)器,并且用 cell 目錄服務(wù)器注冊(cè)其程序名字到機(jī)器的映射。當(dāng)客戶機(jī)想要與一個(gè) RPC 服務(wù)器建立通信,它首先要求其 cell 目錄服務(wù)器來定位服務(wù)器所在的機(jī)器。然后客戶端從 RPC 守護(hù)進(jìn)程處獲得機(jī)器上服務(wù)器進(jìn)程的端口號(hào)。DCE 的跨 cell 還支持更復(fù)雜的搜索。

    DCE RPC 定義了 NDR (Network Data Representation) 用于對(duì)網(wǎng)絡(luò)進(jìn)行編碼來封送信息。與用一個(gè)單一的規(guī)范來表示不同的數(shù)據(jù)類型相比,NDR 支持多規(guī)范(multi-canonical)格式。允許客戶端來選擇使用哪種格式,理想的情況是不需要將它從本地類型來轉(zhuǎn)換。如果這不同于服務(wù)器的本地?cái)?shù)據(jù)表示,服務(wù)器將仍然需要轉(zhuǎn)換,但多規(guī)范格式可以避免當(dāng)客戶端和服務(wù)器都共享相同的本地格式的情況下轉(zhuǎn)換為其他外部格式。例如,在一個(gè)規(guī)定了大端字節(jié)序網(wǎng)絡(luò)數(shù)據(jù)格式的情況下,客戶端和服務(wù)器只支持小端字節(jié)序,那么客戶端必須將每個(gè)數(shù)據(jù)從小端字節(jié)序轉(zhuǎn)為大端字節(jié)序,而當(dāng)服務(wù)器接受到消息后,將每個(gè)數(shù)據(jù)轉(zhuǎn)回小端字節(jié)序。多規(guī)范網(wǎng)絡(luò)數(shù)據(jù)表示將允許客戶端發(fā)送網(wǎng)絡(luò)消息包含小端字節(jié)序格式的數(shù)據(jù)。

    圖6 DCE RPC 中的函數(shù)查找

    第二代 RPC:支持對(duì)象

    面向?qū)ο蟮恼Z言開始在1980年代末興起,很明顯,當(dāng)時(shí)的 Sun ONC 和 DCE RPC 系統(tǒng)都沒有提供任何支持諸如從遠(yuǎn)程類實(shí)例化遠(yuǎn)程對(duì)象、跟蹤對(duì)象的實(shí)例或提供支持多態(tài)性。現(xiàn)有的 RPC 機(jī)制雖然可以運(yùn)作,但他們?nèi)匀徊恢С肿詣?dòng)、透明的方式的面向?qū)ο缶幊碳夹g(shù)。

    微軟 DCOM(COM+)

    1992年4月,微軟發(fā)布 Windows 3.1 包括一種機(jī)制稱為 OLE (Object Linking and Embedding)。這允許一個(gè)程序動(dòng)態(tài)鏈接其他庫來支持的其他功能。如將一個(gè)電子表格嵌入到 Word 文檔。OLE 演變成了 COM (Component Object Model)。一個(gè) COM 對(duì)象是一個(gè)二進(jìn)制文件。使用 COM 服務(wù)的程序來訪問標(biāo)準(zhǔn)化接口的 COM 對(duì)象而不是其內(nèi)部結(jié)構(gòu)。COM 對(duì)象用全局唯一標(biāo)識(shí)符(GUID)來命名,用類的 ID 來識(shí)別對(duì)象的類。幾種方法來創(chuàng)建一個(gè) COM 對(duì)象(例如 CoGetInstanceFromFile)。COM 庫在系統(tǒng)注冊(cè)表中查找相應(yīng)的二進(jìn)制代碼(一個(gè) DLL 或可執(zhí)行文件),來創(chuàng)建對(duì)象,并給調(diào)用者返回一個(gè)接口指針。COM 的著眼點(diǎn)是在于同一臺(tái)計(jì)算機(jī)上不同應(yīng)用程序之間的通訊需求.

    DCOM( Distributed Component Object Model)是 COM 的擴(kuò)展,它支持不同的兩臺(tái)機(jī)器上的組件間的通信,而且不論它們是運(yùn)行在局域網(wǎng)、廣域網(wǎng)、還是 Internet 上。借助 DCOM 你的應(yīng)用程序?qū)⒛軌蜻M(jìn)行任意空間分布。DCOM 于1996年在 Windows NT4.0 中引入的,后來更名為 COM+。由于 DCOM 是為了支持訪問遠(yuǎn)程 COM 對(duì)象,需要?jiǎng)?chuàng)建一個(gè)對(duì)象的過程,此時(shí)需要提供服務(wù)器的網(wǎng)絡(luò)名以及類 ID。微軟提供了一些機(jī)制來實(shí)現(xiàn)這一點(diǎn)。最透明的方式是遠(yuǎn)程計(jì)算機(jī)的名稱固定在注冊(cè)表(或 DCOM 類存儲(chǔ))里,與特定類 ID 相關(guān)聯(lián)。以此方式,應(yīng)用程序不知道它正在訪問一個(gè)遠(yuǎn)程對(duì)象,并且可以使用與訪問本地 COM 對(duì)象相同的接口指針。另一方面,應(yīng)用程序也可指定一個(gè)機(jī)器名作為參數(shù)。

    由于 DCOM 是 COM 這個(gè)組件技術(shù)的無縫升級(jí),所以你能夠從你現(xiàn)有的有關(guān) COM 得知識(shí)中獲益,你的以前在 COM 中開發(fā)的應(yīng)用程序、組件、工具都可以移入分布式的環(huán)境中。DCOM 將為你屏蔽底層網(wǎng)絡(luò)協(xié)議的細(xì)節(jié),你只需要集中精力于你的應(yīng)用。

    DCOM 最大的缺點(diǎn)是這是微軟獨(dú)家的解決辦法,在跨防火墻方面的工作做得不是很好(大多數(shù)RPC系統(tǒng)也有類似的問題),因?yàn)榉阑饓Ρ仨氃试S某些端口來允許 ORPC 和 DCOM 通過。

    CORBA

    雖然 DCE 修復(fù)的一些 Sun RPC 的缺點(diǎn),但某些缺陷依然存在。例如,如果服務(wù)器沒有運(yùn)行,客戶端是無法連接到遠(yuǎn)程過程進(jìn)行調(diào)用的。管理員必須要確保在任何客戶端試圖連接到服務(wù)器之前將服務(wù)器啟動(dòng)。如果一個(gè)新服務(wù)或接口添加到了系統(tǒng),客戶端是不能發(fā)現(xiàn)的。最后,面向?qū)ο笳Z言期望在函數(shù)調(diào)用中體現(xiàn)多態(tài)性,即不同類型的數(shù)據(jù)的函數(shù)的行為應(yīng)該有所不同,而這點(diǎn)恰恰是傳統(tǒng)的 RPC 所不支持的。

    CORBA (Common Object Request Broker Architecture) 就是為了解決上面提到的各種問題。是由 OMG 組織制訂的一種標(biāo)準(zhǔn)的面向?qū)ο髴?yīng)用程 序體系規(guī)范。或者說 CORBA體系結(jié)構(gòu)是對(duì)象管理組織(OMG)為解決分布式處理環(huán)境(DCE)中,硬件和軟件系統(tǒng)的互連而提出的一種解決方案。OMG 成立于1989年,作為一個(gè)非營利性組織,集中致力于開發(fā)在技術(shù)上具有先進(jìn)性、在商業(yè)上具有可行性并且獨(dú)立于廠商的軟件互聯(lián)規(guī)范,推廣面向?qū)ο竽P图夹g(shù),增強(qiáng)軟件的可移植性(Portability)、可重用性(Reusability)和互操作性(Interoperability)。該組織成立之初,成員包括 Unisys、Sun、Cannon、Hewlett-Packard 和 Philips 等在業(yè)界享有聲譽(yù)的軟硬件廠商,目前該組織擁有800多家成員。

    CORBA 體系的主要內(nèi)容包括以下幾部分:

    • 對(duì)象請(qǐng)求代理 (Object Request Broker,ORB):負(fù)責(zé)對(duì)象在分布環(huán)境中透明地收發(fā)請(qǐng)求和響應(yīng),它是構(gòu)建分布對(duì)象應(yīng)用、在異構(gòu)或同構(gòu)環(huán)境下實(shí)現(xiàn)應(yīng)用間互操作的基礎(chǔ)。
    • 對(duì)象服務(wù)(Object Services):為使用和實(shí)現(xiàn)對(duì)象而提供的基本對(duì)象集合,這些服務(wù)應(yīng)獨(dú)立于應(yīng)用領(lǐng)域。主要的 CORBA 服務(wù)有:名錄服務(wù)(Naming Service)、事件服務(wù)(Event Service)、生命周期服務(wù)(Life Cycle Service)、關(guān)系服務(wù)(Relationship Service)以及事務(wù)服務(wù)(Transaction Service)等。這些服務(wù)幾乎包括分布系統(tǒng)和面向?qū)ο笙到y(tǒng)的各個(gè)方面,每個(gè)組成部分都非常復(fù)雜。
    • 公共設(shè)施(Common Facilitites):向終端用戶提供一組共享服務(wù)接口,例如系統(tǒng)管理、組合文檔和電子郵件等。
    • 應(yīng)用接口(Application Interfaces)。由銷售商提供的可控制其接口的產(chǎn)品,相應(yīng)于傳統(tǒng)的應(yīng)用層表示,處于參考模型的最高層。
    • 領(lǐng)域接口(Domain Interfaces):為應(yīng)用領(lǐng)域服務(wù)而提供的接口,如OMG 組織為 PDM 系統(tǒng)制定的規(guī)范。

    當(dāng)客戶端發(fā)出請(qǐng)求時(shí),ORB 做了如下事情:

    • 在客戶端編組參數(shù);
    • 定位服務(wù)器對(duì)象。如果有必要的話,它會(huì)在服務(wù)器創(chuàng)建一個(gè)過程來處理請(qǐng)求;
    • 如果服務(wù)器是遠(yuǎn)程是,就使用 RPC 或 socket 來傳送請(qǐng)求;
    • 在服務(wù)器上將參數(shù)解析成為服務(wù)器格式;
    • 在服務(wù)器上組裝返回值;
    • 如果服務(wù)器是遠(yuǎn)程的,就將返回值傳回;
    • 在客戶端對(duì)返回結(jié)果進(jìn)行解析;

    IDL(Interface Definition Language) 是用于指定類的名字、屬性和方法。它不包含對(duì)象的實(shí)現(xiàn)。IDL 編譯器生成代碼來處理編組、解封以及ORB與網(wǎng)絡(luò)之間的交互。它會(huì)生成客戶機(jī)和服務(wù)器存根。IDL 是編程語言中立,支持包括C、C++、Java、Perl、Python、Ada、COBOL、Smalltalk、Objective C 和 LISP 等語言。一個(gè)示例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數(shù)據(jù)類型包括:

    • 基本類型:long, short, string, float, …
    • 構(gòu)造類型:struct、union、枚舉、序列
    • 對(duì)象引用
    • any 類型:一個(gè)動(dòng)態(tài)類型的值

    編程中最常見的實(shí)現(xiàn)方式是通過對(duì)象引用來實(shí)現(xiàn)請(qǐng)求。下面是一個(gè)使用 IDL 的例子:

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

    在 CORBA 規(guī)范中,沒有明確說明不同廠商的中間件產(chǎn)品要實(shí)現(xiàn)所有的服務(wù)功能,并且允許廠商開發(fā)自己的服務(wù)類型。因此, 不同廠商的 ORB 產(chǎn)品對(duì) CORBA 服務(wù)的支持能力不同,使我們?cè)卺槍?duì)待開發(fā)系統(tǒng)的功能進(jìn)行中間件產(chǎn)品選擇時(shí),有更多的選擇余地。

    CORBA 的不足有:

    • 盡管有多家供應(yīng)商提供CORBA產(chǎn)品,但是仍找不到能夠單獨(dú)為異種網(wǎng)絡(luò)中的所有環(huán)境提供實(shí)現(xiàn)的供應(yīng)商。不同的 CORBA 實(shí)現(xiàn)之間會(huì)出現(xiàn)缺乏互操作性的現(xiàn)象,從而造成一些問題;而且,由于供應(yīng)商常常會(huì)自行定義擴(kuò)展,而 CORBA 又缺乏針對(duì)多線程環(huán)境的規(guī)范,對(duì)于像 C 或 C++ 這樣的語言,源碼兼容性并未完全實(shí)現(xiàn)。
    • CORBA 過于復(fù)雜,要熟悉 CORBA,并進(jìn)行相應(yīng)的設(shè)計(jì)和編程,需要許多個(gè)月來掌握,而要達(dá)到專家水平,則需要好幾年。

    更多有關(guān) CORBA 的優(yōu)缺點(diǎn),可以參閱 Michi Henning 的《The rise and fall of CORBA》。

    Java RMI

    CORBA 旨在提供一組全面的服務(wù)來管理在異構(gòu)環(huán)境中(不同語言、操作系統(tǒng)、網(wǎng)絡(luò))的對(duì)象。Java 在其最初只支持通過 socket 來實(shí)現(xiàn)分布式通信。1995年,作為 Java 的締造者,Sun 公司開始創(chuàng)建一個(gè) Java 的擴(kuò)展,稱為 Java RMI(Remote Method Invocation,遠(yuǎn)程方法調(diào)用)。Java RMI 允許程序員創(chuàng)建分布式應(yīng)用程序時(shí),可以從其他 Java 虛擬機(jī)(JVM)調(diào)用遠(yuǎn)程對(duì)象的方法。

    一旦應(yīng)用程序(客戶端)引用了遠(yuǎn)程對(duì)象,就可以進(jìn)行遠(yuǎn)程調(diào)用了。這是通過 RMI 提供的命名服務(wù)(RMI 注冊(cè)中心)來查找遠(yuǎn)程對(duì)象,來接收作為返回值的引用。Java RMI 在概念上類似于 RPC,但能在不同地址空間支持對(duì)象調(diào)用的語義。

    與大多數(shù)其他諸如 CORBA 的 RPC 系統(tǒng)不同,RMI 只支持基于 Java 來構(gòu)建,但也正是這個(gè)原因, RMI 對(duì)于語言來說更加整潔,無需做額外的數(shù)據(jù)序列化工作。Java RMI 的設(shè)計(jì)目標(biāo)應(yīng)該是:

    • 能夠適應(yīng)語言、集成到語言、易于使用;
    • 支持無縫的遠(yuǎn)程調(diào)用對(duì)象;
    • 支持服務(wù)器到 applet 的回調(diào);
    • 保障 Java 對(duì)象的安全環(huán)境;
    • 支持分布式垃圾回收;
    • 支持多種傳輸。

    分布式對(duì)象模型與本地 Java 對(duì)象模型相似點(diǎn)在于:

    • 引用一個(gè)對(duì)象可以作為參數(shù)傳遞或作為返回的結(jié)果;
    • 遠(yuǎn)程對(duì)象可以投到任何使用 Java 語法實(shí)現(xiàn)的遠(yuǎn)程接口的集合上;
    • 內(nèi)置 Java instanceof 操作符可以用來測試遠(yuǎn)程對(duì)象是否支持遠(yuǎn)程接口。

    不同點(diǎn)在于:

    • 遠(yuǎn)程對(duì)象的類是與遠(yuǎn)程接口進(jìn)行交互,而不是與這些接口的實(shí)現(xiàn)類交互;
    • Non-remote 參數(shù)對(duì)于遠(yuǎn)程方法調(diào)用來說是通過復(fù)制,而不是通過引用;
    • 遠(yuǎn)程對(duì)象是通過引用來傳遞,而不是復(fù)制實(shí)際的遠(yuǎn)程實(shí)現(xiàn);
    • 客戶端必須處理額外的異常。

    接口和類

    所有的遠(yuǎn)程接口都繼承自?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

    注意,每個(gè)方法必須在 throws 里面聲明?java.rmi.RemoteException?。 只要客戶端調(diào)用遠(yuǎn)程方法出現(xiàn)失敗,這個(gè)異常就會(huì)拋出。

    遠(yuǎn)程對(duì)象類

    Java.rmi.server.RemoteObject?類提供了遠(yuǎn)程對(duì)象實(shí)現(xiàn)的語義包括hashCode、equals 和 toString。?java.rmi.server.RemoteServer 及其子類提供讓對(duì)象實(shí)現(xiàn)遠(yuǎn)程可見。java.rmi.server.UnicastRemoteObject?類定義了客戶機(jī)與服務(wù)器對(duì)象實(shí)例建立一對(duì)一的連接.

    存根

    Java RMI 通過創(chuàng)建存根函數(shù)來工作。存根由 rmic 編譯器生成。自 Java 1.5 以來,Java 支持在運(yùn)行時(shí)動(dòng)態(tài)生成存根類。編譯器 rmic 會(huì)提供各種編譯選項(xiàng)。

    定位對(duì)象

    引導(dǎo)名稱服務(wù)提供了用于存儲(chǔ)對(duì)遠(yuǎn)程對(duì)象的命名引用。一個(gè)遠(yuǎn)程對(duì)象引用可以存儲(chǔ)使用類?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 工作流程

    RMI 架構(gòu)

    RMI 是一個(gè)三層架構(gòu)(圖8)。最上面是 Stub/Skeleton layer(存根/骨架層)。方法調(diào)用從 Stub、Remote Reference Layer (遠(yuǎn)程引用層)和 Transport Layer(傳輸層)向下,傳遞給主機(jī),然后再次經(jīng)傳 Transport Layer 層,向上穿過 Remote Reference Layer 和 Skeleton ,到達(dá)服務(wù)器對(duì)象。 Stub 扮演著遠(yuǎn)程服務(wù)器對(duì)象的代理的角色,使該對(duì)象可被客戶激活。Remote Reference Layer 處理語義、管理單一或多重對(duì)象的通信,決定調(diào)用是應(yīng)發(fā)往一個(gè)服務(wù)器還是多個(gè)。Transport Layer 管理實(shí)際的連接,并且追蹤可以接受方法調(diào)用的遠(yuǎn)程對(duì)象。服務(wù)器端的 Skeleton 完成對(duì)服務(wù)器對(duì)象實(shí)際的方法調(diào)用,并獲取返回值。返回值向下經(jīng) Remote Reference Layer 、服務(wù)器端的 Transport Layer 傳遞回客戶端,再向上經(jīng) Transport Layer 和 Remote Reference Layer 返回。最后,Stub 程序獲得返回值。

    要完成以上步驟需要有以下幾個(gè)步驟:

    • 生成一個(gè)遠(yuǎn)程接口;
    • 實(shí)現(xiàn)遠(yuǎn)程對(duì)象(服務(wù)器端程序);
    • 生成 Stub 和 Skeleton(服務(wù)器端程序);
    • 編寫服務(wù)器程序 ;
    • 編寫客戶程序 ;
    • 注冊(cè)遠(yuǎn)程對(duì)象;
    • 啟動(dòng)遠(yuǎn)程對(duì)象

    圖8 Java RMI 架構(gòu)

    RMI 分布式垃圾回收

    根據(jù) Java 虛擬機(jī)的垃圾回收機(jī)制原理,在分布式環(huán)境下,服務(wù)器進(jìn)程需要知道哪些對(duì)象不再由客戶端引用,從而可以被刪除(垃圾回收)。在 JVM中,Java 使用引用計(jì)數(shù)。當(dāng)引用計(jì)數(shù)歸零時(shí),對(duì)象將會(huì)垃圾回收。在RMI,Java 支持兩種操作:dirty 和 clean。本地 JVM 定期發(fā)送一個(gè) dirty 到服務(wù)器來說明該對(duì)象仍在使用。定期重發(fā) dirty 的周期是由服務(wù)器租賃時(shí)間來決定的。當(dāng)客戶端沒有需要更多的本地引用遠(yuǎn)程對(duì)象時(shí),它發(fā)送一個(gè) clean 調(diào)用給服務(wù)器。不像 DCOM,服務(wù)器不需要計(jì)算每個(gè)客戶機(jī)使用的對(duì)象,只是簡單的做下通知。如果它租賃時(shí)間到期之前沒有接收到任何 dirty 或者 clean 的消息,則可以安排將對(duì)象刪除。

    第三代 RPC 以及 Web Services

    由于互聯(lián)網(wǎng)的興起,Web 瀏覽器成為占主導(dǎo)地位的用于訪問信息的模型。現(xiàn)在的應(yīng)用設(shè)計(jì)的首要任務(wù)大多數(shù)是提供用戶通過瀏覽器來訪問,而不是編程訪問或操作數(shù)據(jù)。

    網(wǎng)頁設(shè)計(jì)關(guān)注的是內(nèi)容。解析展現(xiàn)方面往往是繁瑣的。傳統(tǒng) RPC 解決方案可以工作在互聯(lián)網(wǎng)上,但問題是,他們通常嚴(yán)重依賴于動(dòng)態(tài)端口分配,往往要進(jìn)行額外的防火墻配置。

    Web Services 成為一組協(xié)議,允許服務(wù)被發(fā)布、發(fā)現(xiàn),并用于技術(shù)無關(guān)的形式。即服務(wù)不應(yīng)該依賴于客戶的語言、操作系統(tǒng)或機(jī)器架構(gòu)。

    Web Services 的實(shí)現(xiàn)一般是使用 Web 服務(wù)器作為服務(wù)請(qǐng)求的管道。客戶端訪問該服務(wù),首先是通過一個(gè) HTTP 協(xié)議發(fā)送請(qǐng)求到服務(wù)器上的 Web 服務(wù)器。Web 服務(wù)器配置識(shí)別 URL 的一部分路徑名或文件名后綴并將請(qǐng)求傳遞給特定的瀏覽器插件模塊。這個(gè)模塊可以除去頭、解析數(shù)據(jù)(如果需要),并根據(jù)需要調(diào)用其他函數(shù)或模塊。對(duì)于這個(gè)實(shí)現(xiàn)流,一個(gè)常見的例子是瀏覽器對(duì)于 Java Servlet 的支持。HTTP 請(qǐng)求會(huì)被轉(zhuǎn)發(fā)到 JVM 運(yùn)行的服務(wù)端代碼來執(zhí)行處理。

    XML-RPC

    XML-RPC 是1998年作為一個(gè) RPC 消息傳遞協(xié)議,將請(qǐng)求和響應(yīng)封裝解析為人類可讀的 XML格式。XML 格式基于 HTTP 協(xié)議,緩解了傳統(tǒng)企業(yè)的防火墻需要為 RPC 服務(wù)器應(yīng)用程序打開額外的端口的問題。

    下面是一個(gè) 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

    這個(gè)例子中,方法 sumAndDifference 有兩個(gè)整數(shù)參數(shù) 5 和 3。

    XML-RPC 支持的基本數(shù)據(jù)類型是:int、string、boolean、double 和 dateTime.iso8601。此外,還有 base64 類型用于編碼任意二進(jìn)制數(shù)據(jù)。array 和 struct 允許定義數(shù)組和結(jié)構(gòu)。

    XML-RPC 不限制語任何特定的語言,也不是一套完整的軟件來處理遠(yuǎn)程過程,諸如存根生成、對(duì)象管理和服務(wù)查找都不在協(xié)議內(nèi)。現(xiàn)在有很多庫針可以針對(duì)不同的語言,比如 Apache XML-RPC 可以用于 Java、Python 和 Perl。

    XML-RPC 是一個(gè)簡單的規(guī)范(約7頁),沒有雄心勃勃的目標(biāo)——它只關(guān)注消息,而并不處理諸如垃圾收集、遠(yuǎn)程對(duì)象、遠(yuǎn)程過程的名稱服務(wù)和其他方面的問題。然而,即使沒有廣泛的產(chǎn)業(yè)支持,簡單的協(xié)議卻能廣泛采用。

    SOAP

    SOAP(Simple Object Access Protocol,簡單對(duì)象訪問協(xié)議),是以 XML-RPC 規(guī)范作為創(chuàng)建 SOAP 的依據(jù),成立于1998年,獲得微軟和 IBM 的大力支持。該協(xié)議在創(chuàng)建初期只作為一種對(duì)象訪問協(xié)議,但由于 SOAP 的發(fā)展,其協(xié)議已經(jīng)不單只是用于簡單的訪問對(duì)象,所以這種 SOAP 縮寫已經(jīng)在標(biāo)準(zhǔn)的1.2版后被廢止了。1.2版在2003年6月24日成為 W3C 的推薦版本。SOAP 指定 XML 作為無狀態(tài)的消息交換格式,包括了 RPC 式的過程調(diào)用。

    有關(guān) SOAP 的標(biāo)準(zhǔn)可以參閱?https://www.w3.org/TR/soap/。

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

    下面是一個(gè)簡單的例子:

    <?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 消息中的根節(jié)點(diǎn),是 SOAP 消息中必須的部分。<soap:Header>是 SOAP 消息中可選部分,是指消息頭。<soap:Body>是 SOAP 中必須部分,是指消息體。

    上面例子的 SOAP 消息結(jié)構(gòu)如下:

    圖9 SOAP 消息結(jié)構(gòu)

    SOAP 它只是提供了一個(gè)標(biāo)準(zhǔn)化的消息結(jié)構(gòu),為了實(shí)現(xiàn)它往往需要用 WSDL 來描述 Web Services 的方法。WSDL (Web Services Description Language) 是基于 XML 的一種用于描述 Web Services 以及如何訪問 Web Services 的語言。

    WSDL 文檔包括以下幾個(gè)部分:

    • 類型(Types):定義了 Web Services 使用的數(shù)據(jù)類型;
    • 消息(n/a):描述使用消息的數(shù)據(jù)元素或參數(shù);
    • 接口(Interface):描述服務(wù)提供的操作。這包括操作以及每個(gè)操作所使用的輸入和輸出消息;
    • 綁定(Binding):為每個(gè)端口定義消息格式和協(xié)議細(xì)節(jié)。例如,它可以定義 RPC 式的消息;
    • 服務(wù)(Service):系統(tǒng)功能相關(guān)的集合,包括其關(guān)聯(lián)的接口、操作、消息等;
    • 終點(diǎn)(Endpoint):定義了地址或者 Web Services 的連接點(diǎn);
    • 操作(Operation):定義了 SOAP 的動(dòng)作,以及消息編碼的方式。

    下面是一個(gè) 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

    從微軟的產(chǎn)品角度來看,可以說 .NET Remoting 就是 DCOM 的一種升級(jí),它改善了很多功能,并極好的融合到 .NET 平臺(tái)下。Microsoft .NET Remoting 提供了一種允許對(duì)象通過應(yīng)用程序域與另一對(duì)象進(jìn)行交互的框架。

    .NET Remoting 提供了一種允許對(duì)象通過應(yīng)用程序域與另一對(duì)象進(jìn)行交互的框架。這種框架提供了多種服務(wù),包括激活和生存期支持,以及負(fù)責(zé)與遠(yuǎn)程應(yīng)用程序進(jìn)行消息傳輸?shù)耐ㄓ嵧ǖ馈8袷交绦蛴糜谠谙⑼ㄟ^通道傳輸之前,對(duì)其進(jìn)行編碼和解碼。應(yīng)用程序可以在注重性能的場合使用二進(jìn)制編碼,在需要與其他遠(yuǎn)程處理框架進(jìn)行交互的場合使用 XML 編碼。在從一個(gè)應(yīng)用程序域向另一個(gè)應(yīng)用程序域傳輸消息時(shí),所有的 XML 編碼都使用 SOAP 協(xié)議。出于安全性方面的考慮,遠(yuǎn)程處理提供了大量掛鉤,使得在消息流通過通道進(jìn)行傳輸之前,安全接收器能夠訪問消息和序列化流

    .NET Remoting 對(duì)象

    有三類對(duì)象可以配置為 .NET Remoting 對(duì)象。您可以根據(jù)應(yīng)用程序的需要來選擇對(duì)象類型:

    • Single Call(單一調(diào)用對(duì)象):?
      Single Call 能且只能為一個(gè)請(qǐng)求提供服務(wù)。在需要對(duì)象完成的工作量有限的情況下 Single Call 非常有用。Single Call 對(duì)象通常不要求存儲(chǔ)狀態(tài)信息,并且不能在方法調(diào)用之間保留狀態(tài)信息。但是,Single Call對(duì)象可以配置為負(fù)載平衡模式。
    • Singleton Objects(單一元素對(duì)象):?
      Singleton Objects 可以為多個(gè)客戶端提供服務(wù),因此可以通過保存客戶端調(diào)用的狀態(tài)信息來實(shí)現(xiàn)數(shù)據(jù)共享。當(dāng)客戶端之間需要明確地共享數(shù)據(jù),并且不能忽略創(chuàng)建和維護(hù)對(duì)象的開銷時(shí),這類對(duì)象非常有用。
    • Client-Activated Objects (CAO,客戶端激活的對(duì)象):?
      CAO 是服務(wù)器端的對(duì)象,將根據(jù)來自客戶端的請(qǐng)求激活這些對(duì)象。這種激活服務(wù)器對(duì)象的方法與傳統(tǒng)的 COM coclass 激活方法非常相似。當(dāng)客戶端使用“new”運(yùn)算符提交對(duì)服務(wù)器對(duì)象的請(qǐng)求時(shí),會(huì)向遠(yuǎn)程應(yīng)用程序發(fā)送一個(gè)激活請(qǐng)求消息。隨后,服務(wù)器創(chuàng)建被請(qǐng)求類的實(shí)例,并向調(diào)用它的客戶端應(yīng)用程序返回 ObjRef。然后,使用此 ObjRef 在客戶端上創(chuàng)建代理。客戶端的方法調(diào)用將在代理上執(zhí)行。客戶端激活的對(duì)象可以為特定的客戶端(不能跨越不同的客戶端對(duì)象)保存方法調(diào)用之間的狀態(tài)信息。每個(gè)“new”調(diào)用都會(huì)向服務(wù)器類型的獨(dú)立實(shí)例返回代理。

    在 .NET Remoting 中,可以通過以下方式在應(yīng)用程序之間傳遞對(duì)象:

    • 作為方法調(diào)用中的參數(shù),例如:public int myRemoteMethod (MyRemoteObject myObj)
    • 方法調(diào)用的返回值,例如:public MyRemoteObject myRemoteMethod(String myString)
    • 訪問 .NET 組件的屬性或字段得到的值,例如:myObj.myNestedObject

    對(duì)于 Marshal By Value (MBV,按值封送)的對(duì)象,當(dāng)它在應(yīng)用程序之間傳遞時(shí),將創(chuàng)建一個(gè)完整的副本。

    對(duì)于 Marshal By Reference (MBR,按引用封送)的對(duì)象,當(dāng)它在應(yīng)用程序之間傳遞時(shí),將創(chuàng)建該對(duì)象的引用。當(dāng)對(duì)象引用 (ObjRef) 到達(dá)遠(yuǎn)程應(yīng)用程序后,將轉(zhuǎn)變成“代理”返回給原始對(duì)象。?
      ?
    下面是一個(gè)簡單 .NET Remoting 服務(wù)器對(duì)象的代碼示例:

    using System; using System.Runtime.Remoting; namespace myRemoteService {// Well Known Web Service objectpublic 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

    下面是客戶端代碼來訪問這個(gè)對(duì)象:

    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

    租用生存期

    對(duì)于那些具有在應(yīng)用程序之外傳送的對(duì)象引用的對(duì)象,將創(chuàng)建一個(gè)租用。租用具有一個(gè)租用時(shí)間。如果租用時(shí)間為 0,則租用過期,對(duì)象就斷開與 .NET Romoting 框架的連接。一旦 AppDomain 中的所有對(duì)象引用都被釋放,則下次垃圾回收時(shí),該對(duì)象將被回收。租用控制了對(duì)象的生存期。

    對(duì)象有默認(rèn)的租用階段。當(dāng)客戶端要在同一服務(wù)器對(duì)象中維護(hù)狀態(tài)時(shí),可以通過許多方法擴(kuò)展租用階段,使對(duì)象繼續(xù)生存:

    • 服務(wù)器對(duì)象可以將其租用時(shí)間設(shè)置為無限,這樣 Remoting 在垃圾回收時(shí)就不會(huì)回收此對(duì)象。
    • 客戶端可以調(diào)用?RemotingServices.GetLifetimeService?方法,從 AppDomain 的租用管理器獲取服務(wù)器對(duì)象的租用。然后,客戶端可以通過 Lease 對(duì)象調(diào)用 Lease.Renew 方法以延長租用。
    • 客戶端可用 AppDomain 的租用管理器為特定的租用注冊(cè)負(fù)責(zé)人。當(dāng) Remoting 對(duì)象的租用過期時(shí),租用管理器將通知負(fù)責(zé)人提出續(xù)租的申請(qǐng)。
    • 如果設(shè)置了?ILease::RenewOnCallTime?屬性,則每次調(diào)用 Remoting 對(duì)象時(shí),都會(huì)用 RenewOnCallTime 屬性指定的總時(shí)間更新租用。

    集成 .NET Remoting 對(duì)象

    .NET Remoting 對(duì)象可以集成在:

    • 托管可執(zhí)行項(xiàng):?
      .NET Remoting 對(duì)象可以集成在任何常規(guī)的 .NET EXE 或托管服務(wù)中。
    • .NET 組件服務(wù):?
      .NET Remoting 對(duì)象可以集成在 .NET 組件服務(wù)基礎(chǔ)結(jié)構(gòu)中,以便利用各種 COM+ 服務(wù),例如:事務(wù)、JIT 和對(duì)象池等。
    • IIS : .NET Remoting 對(duì)象可以集成在 Internet Information Server (IIS) 中。默認(rèn)情況下,集成在 IIS 中的 Remoting 對(duì)象通過 HTTP 通道接收消息。要在 IIS 中集成 Remoting 對(duì)象,必須創(chuàng)建一個(gè)虛擬的根,并將 remoting.config 文件復(fù)制到其中。包含 Remoting 對(duì)象的可執(zhí)行文件或 DLL 應(yīng)放在 IIS 根指向的目錄下的 bin 目錄中。需要注意的是,IIS 根名稱應(yīng)該與配置文件中指定的應(yīng)用程序名稱相同。當(dāng)?shù)谝粋€(gè)消息到達(dá)應(yīng)用程序時(shí),Remoting 配置文件會(huì)自動(dòng)加載。使用這種方法,可以提供 .NET Remoting 對(duì)象作為 Web 服務(wù)。

    下面是一個(gè) 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

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

    .NET 應(yīng)用程序和 AppDomain 之間使用消息進(jìn)行通信。.NET 通道服務(wù)為這一通信過程提供了基礎(chǔ)傳輸機(jī)制。

    .NET 框架提供了 HTTP 和 TCP 通道,但是第三方也可以編寫并使用自己的通道。默認(rèn)情況下,HTTP 通道使用 SOAP 進(jìn)行通信,TCP 通道使用二進(jìn)制有效負(fù)載。

    通過使用可以編寫到集成混合應(yīng)用程序中的自定義通道,可以插入通道服務(wù)(使用 IChannel)。

    下面是一個(gè)加載通道服務(wù)的例子:

    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 序列化格式化程序?qū)?.NET 應(yīng)用程序和 AppDomain 之間的消息進(jìn)行編碼和解碼。在 .NET 運(yùn)行時(shí)中有兩個(gè)本地格式化程序,分別為二進(jìn)制 (System.Runtime.Serialization.Formatters.Binary) 和 SOAP (System.Runtime.Serialization.Formatters.Soap)。

    通過實(shí)現(xiàn) IRemotingFormatter 接口,并將其插入到上文介紹的通道中,可以插入序列化格式化程序。這樣,您可以靈活地選擇通道和格式化程序的組合方式,采用最適合應(yīng)用程序的方案。

    例如:您可以采用 HTTP 通道和二進(jìn)制格式化程序(串行化二進(jìn)制數(shù)據(jù)),也可以采用 TCP 通道和 SOAP 格式。

    Remoting 上下文

    上下文是一個(gè)包含共享公共運(yùn)行時(shí)屬性的對(duì)象的范圍。某些上下文屬性的例子是與同步和線程緊密相關(guān)的。當(dāng) .NET 對(duì)象被激活時(shí),運(yùn)行時(shí)將檢查當(dāng)前上下文是否一致,如果不一致,將創(chuàng)建新的上下文。多個(gè)對(duì)象可以同時(shí)在一個(gè)上下文中運(yùn)行,并且一個(gè) AppDomain 中可以有多個(gè)上下文。

    一個(gè)上下文中的對(duì)象調(diào)用另一個(gè)上下文中的對(duì)象時(shí),調(diào)用將通過上下文代理來執(zhí)行,并且會(huì)受組合的上下文屬性的強(qiáng)制策略影響。新對(duì)象的上下文通常是基于類的元數(shù)據(jù)屬性選擇的。

    可以與上下文綁定的類稱作上下文綁定類。上下文綁定類可以具有稱為上下文屬性的專用自定義屬性。上下文屬性是完全可擴(kuò)展的,您可以創(chuàng)建這些屬性并將它們附加到自己的類中。與上下文綁定的對(duì)象是從?System.ContextBoundObject?導(dǎo)出的。

    .NET Remoting 元數(shù)據(jù)和配置文件

    元數(shù)據(jù)

    .NET 框架使用元數(shù)據(jù)和程序集來存儲(chǔ)有關(guān)組件的信息,使得跨語言編程成為可能。.NET Remoting 使用元數(shù)據(jù)來動(dòng)態(tài)創(chuàng)建代理對(duì)象。在客戶端創(chuàng)建的代理對(duì)象具有與原始類相同的成員。但是,代理對(duì)象的實(shí)現(xiàn)僅僅將所有請(qǐng)求通過 .NET Remoting 運(yùn)行時(shí)轉(zhuǎn)發(fā)給原始對(duì)象。序列化格式化程序使用元數(shù)據(jù)在方法調(diào)用和有效負(fù)載數(shù)據(jù)流之間來回轉(zhuǎn)換。

    客戶端可以通過以下方法獲取訪問 Remoting 對(duì)象所需的元數(shù)據(jù)信息:

    • 服務(wù)器對(duì)象的 .NET 程序集 : 服務(wù)器對(duì)象可以創(chuàng)建元數(shù)據(jù)程序集,并將其分發(fā)給客戶端。在編譯客戶端對(duì)象時(shí),客戶端對(duì)象可以引用這些程序集。在客戶端和服務(wù)器都是托管組件的封閉環(huán)境中,這種方法非常有用。
    • Remoting 對(duì)象可以提供 WSDL文件,用于說明對(duì)象及其方法。所有可以根據(jù) WSDL 文件讀取和生成 SOAP 請(qǐng)求的客戶端都可以調(diào)用此對(duì)象,或使用 SOAP 與之通信。使用與 .NET SDK 一同分發(fā)的 SOAPSUDS.EXE 工具,.NET Remoting 服務(wù)器對(duì)象可以生成具有元數(shù)據(jù)功能的 WSDL 文件。當(dāng)某個(gè)組織希望提供所有客戶都能訪問和使用的公共服務(wù)時(shí),這種方法非常有用。
    • .NET 客戶可以使用 SOAPSUDS 工具從服務(wù)器上下載 XML 架構(gòu)(在服務(wù)器上生成),生成僅包含元數(shù)據(jù)(沒有代碼)的源文件或程序集。您可以根據(jù)需要將源文件編譯到客戶端應(yīng)用程序中。如果多層應(yīng)用程序中某一層的對(duì)象需要訪問其他層的 Remoting 對(duì)象,則經(jīng)常使用此方法。
    配置文件

      ?
    配置文件(.config 文件)用于指定給定對(duì)象的各種 Remoting 特有信息。通常情況下,每個(gè) AppDomain 都有自己的配置文件。使用配置文件有助于實(shí)現(xiàn)位置的透明性。配置文件中指定的詳細(xì)信息也可以通過編程來完成。使用配置文件的主要好處在于,它將與客戶端代碼無關(guān)的配置信息分離出來,這樣在日后更改時(shí)僅需要修改配置文件,而不用編輯和重新編譯源文件。.NET Remoting 的客戶端和服務(wù)器對(duì)象都使用配置文件。

    典型的配置文件包含以下信息及其他信息:

    • 集成應(yīng)用程序信息
    • 對(duì)象名稱
    • 對(duì)象的 URI
    • 注冊(cè)的通道(可以同時(shí)注冊(cè)多個(gè)通道)
    • 服務(wù)器對(duì)象的租用時(shí)間信息

    下面是一個(gè)配置文件示例:

    <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 如何工作之后,讓我們來看一下各種方案,分析如何在不同的方案中充分發(fā)揮 .NET Remoting 的優(yōu)勢。下表列出了可能的客戶端/服務(wù)器組合,以及默認(rèn)情況下采用的底層協(xié)議和有效負(fù)載。請(qǐng)注意,.NET Remoting 框架是可擴(kuò)展的,您可以編寫自己的通信通道和序列化格式化程序。

    客戶端服務(wù)器有效負(fù)載協(xié)議
    .NET 組件.NET 組件SOAP/XMLhttp
    .NET 組件.NET 組件二進(jìn)制TCP
    托管/非托管.NET Web 服務(wù)SOAP/XMLhttp
    .NET 組件非托管的傳統(tǒng) COM 組件NDR(網(wǎng)絡(luò)數(shù)據(jù)表示形式)DCOM
    非托管的傳統(tǒng) COM 組件.NET 組件NDRDCOM
    使用 HTTP-SOAP

    Web 服務(wù)是可以通過 URL 尋址的資源,并通過編程向需要使用這些資源的客戶端返回信息。客戶端使用 Web Services 時(shí)不必考慮其實(shí)現(xiàn)細(xì)節(jié)。Web Services 使用稱為“合約”的嚴(yán)格定義的接口,此接口采用 Web 服務(wù)說明語言 (WSDL) 文件描述。

    .NET Remoting 對(duì)象可以集成在 IIS 中,作為 Web 服務(wù)提供。任何可以使用 WSDL 文件的客戶端都可以按照 WSDL 文件中指定的合約,對(duì) Remoting 對(duì)象執(zhí)行 SOAP 調(diào)用。IIS 使用 ISAPI 擴(kuò)展將這些請(qǐng)求路由到相應(yīng)的對(duì)象。這樣,Remoting 對(duì)象就可以作為 Web 服務(wù)對(duì)象來使用,從而充分發(fā)揮 .NET 框架基礎(chǔ)結(jié)構(gòu)的作用。如果您希望不同平臺(tái)/環(huán)境的程序均能夠訪問對(duì)象,可以采用這種配置。這種配置便于客戶端通過防火墻訪問您的 .NET 對(duì)象。

    圖10 .NET 使用 HTTP-SOAP

    使用 SOAP-HTTP 通道

    默認(rèn)情況下,HTTP 通道使用 SOAP 格式化程序。因此,如果客戶端需要通過 Internet 訪問對(duì)象,可以使用 HTTP 通道。因?yàn)檫@種方法使用 HTTP,所以此配置允許通過防火墻遠(yuǎn)程訪問 .NET 對(duì)象。只需要按前一節(jié)中介紹的方法將這些對(duì)象集成在 IIS 中,即可將它配置為 Web 服務(wù)對(duì)象。隨后,客戶端就可以讀取這些對(duì)象的 WSDL 文件,使用 SOAP 與 Remoting 對(duì)象通信。

    使用 TCP 通道

    默認(rèn)情況下,TCP 通道使用二進(jìn)制格式化程序。此格式化程序以二進(jìn)制格式對(duì)數(shù)據(jù)進(jìn)行序列化,并使用原始 socket 在網(wǎng)絡(luò)中傳送數(shù)據(jù)。如果對(duì)象部署在受防火墻保護(hù)的封閉環(huán)境中,此方法是理想的選擇。這種方法使用 socket 在對(duì)象之間傳遞二進(jìn)制數(shù)據(jù),因此性能極佳。由于它使用 TCP 通道來提供對(duì)象,因此在封閉環(huán)境中具有低開銷的優(yōu)點(diǎn)。由于防火墻和配置的問題,此方法不宜在 Internet 上使用。

    圖11 .NET 使用 TCP 通道

    使用非托管的 COM 組件

    可以通過 COM Interop Service 調(diào)用非托管的傳統(tǒng) COM 組件。當(dāng) .NET Remoting 客戶端對(duì)象創(chuàng)建 COM 對(duì)象的實(shí)例時(shí),該對(duì)象通過運(yùn)行時(shí)可調(diào)用包裝程序 (RCW) 來提供。其中,RCW 擔(dān)當(dāng)真正的非托管對(duì)象的代理。對(duì)于 .NET 客戶,這些包裝程序看起來和 .NET 客戶端的任何其他托管類一樣。但實(shí)際上,它們僅僅是托管 (.NET) 和非托管 (COM) 代碼之間的封送調(diào)用。

    同樣地,您可以將 .NET Remoting 服務(wù)器對(duì)象提供給傳統(tǒng) COM 客戶端。當(dāng) COM 客戶端創(chuàng)建 .NET 對(duì)象的實(shí)例時(shí),該對(duì)象通過 COM 可調(diào)用包裝程序 (CCW) 來提供。其中,CCW 擔(dān)當(dāng)真正的托管對(duì)象的代理。?
      ?
    這兩種方案都使用 DCOM 通信。如果環(huán)境中既有傳統(tǒng)的 COM 組件,又有 .NET 組件,那么這種互操作性將為您提供便利。?
      

    總結(jié)

    Microsoft .NET 框架提供了強(qiáng)大、可擴(kuò)展、獨(dú)立于語言的框架,適合開發(fā)可靠、可伸縮的分布式系統(tǒng)。.NET Romoting 框架提供了根據(jù)系統(tǒng)需求進(jìn)行遠(yuǎn)程交互的強(qiáng)大手段。.NET Remoting 實(shí)現(xiàn)了與 Web 服務(wù)的無縫集成,并提供了一種方法,可以提供 .NET 對(duì)象以供跨平臺(tái)訪問。

    Java 中的 XML Web Services

    Java RMI 與遠(yuǎn)程對(duì)象進(jìn)行交互,其實(shí)現(xiàn)是需要基于 Java 的模型。此外,它沒有使用 Web Services 和基于 HTTP 的消息傳遞。現(xiàn)在,已經(jīng)出現(xiàn)了大量的軟件來支持基于 Java 的 Web Services。JAX-WS (Java API for XML Web Services) 就是作為 Web Services 消息息和遠(yuǎn)程過程調(diào)用的規(guī)范。它允許一個(gè)調(diào)用基于Java的web服務(wù)使用Java RMI(即。,相對(duì)透明的程序員)。JAX-WS 的一個(gè)目標(biāo)是平臺(tái)互操作性。其 API 使用 SOAP 和WSDL。雙方不需要 Java 環(huán)境。

    創(chuàng)建一個(gè) RPC 端點(diǎn)

    在服務(wù)器端,進(jìn)行下面的步驟來創(chuàng)建一個(gè) RPC 端點(diǎn):

    • 定義一個(gè)接口(Java接口);
    • 實(shí)現(xiàn)服務(wù);
    • 創(chuàng)建一個(gè)發(fā)布者,用于創(chuàng)建服務(wù)的實(shí)例,并發(fā)布一個(gè)服務(wù)名字。

    在客戶端:

    • 創(chuàng)建一個(gè)代理(客戶端存根)。wsimport 命令根據(jù) WSDL 文檔,創(chuàng)建一個(gè)客戶機(jī)存根;
    • 編寫一個(gè)客戶端,通過代理創(chuàng)建遠(yuǎn)程服務(wù)的一個(gè)實(shí)例(存根),調(diào)用它的方法。

    JAX-RPC 執(zhí)行流程如下:

    • Java 客戶機(jī)調(diào)用存根上的方法(代理);
    • 存根調(diào)用適當(dāng)?shù)?Web 服務(wù);
    • Web 服務(wù)器被調(diào)用并指導(dǎo) JAX-WS 框架;
    • 框架調(diào)用實(shí)現(xiàn);
    • 實(shí)現(xiàn)返回結(jié)果給該框架;
    • 該框架將結(jié)果返回給 Web 服務(wù)器;
    • 服務(wù)器將結(jié)果發(fā)送給客戶端存根;
    • 客戶端存根返回信息給調(diào)用者;

    圖12 JAX-WS 調(diào)用流程

    超越 SOAP

    SOAP 雖然仍然是廣泛部署應(yīng)用,但在許多環(huán)境中很多廠商已經(jīng)拋棄了 SOAP,轉(zhuǎn)而使用其他更輕量、更容易理解、或者與 Web 交互模型更干凈的機(jī)制。例如,Google 的 API 在2006年后就不再支持 SOAP 接口,而是使用AJAX、XML-RPC 和 REST 作為替代。一個(gè)匿名的微軟員工批評(píng) SOAP 過于復(fù)雜,因?yàn)椤拔覀兿M覀兊墓ぞ邅黹喿x它,而不是人”。不管上述言論是否準(zhǔn)確,有一點(diǎn)是可以肯定的,SOAP 顯然是一個(gè)復(fù)雜和高度冗長的格式。

    AJAX

    Web 瀏覽器最初的設(shè)計(jì),是為 Web 頁面提供非動(dòng)態(tài)的交互模型。Web 瀏覽器是建立在同步的請(qǐng)求-響應(yīng)(request-response)的交互模型。發(fā)送一個(gè)請(qǐng)求到服務(wù)器,服務(wù)器返回整個(gè)頁面。在當(dāng)時(shí)沒有更新部分頁面的好方法,而唯一可行的方法是利用幀,即將不同的頁面加載到每一幀,其實(shí)現(xiàn)是笨重的,也有很大的限制性。而改變了這一切的關(guān)鍵因素是:

    • 文檔對(duì)象模型(Document Object Model)和 JavaScript 的出現(xiàn),使得可以以編程方式來更改 Web 頁面的各個(gè)部分;
    • AJAX 提供了與服務(wù)器以非阻塞方式進(jìn)行交互,即允許底層 JavaScript 在等待服務(wù)器結(jié)果時(shí),用戶仍然可以與頁面進(jìn)行交互。

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

    • 它是異步的,因?yàn)榭蛻舳说却?wù)器結(jié)果不會(huì)被阻塞;
    • AJAX 集成到了 JavaScript,作為瀏覽器解釋 Web 頁面的一部分。JavaScript 使用 HTTPRequest 來調(diào)用 AJAX 請(qǐng)求。JavaScript 也可能修改文檔對(duì)象模型,定義了頁面的樣子;
    • 數(shù)據(jù)以 XML 文檔形式發(fā)送和接收。(在后期發(fā)展中,AJAX 也支持其他的數(shù)據(jù)格式,比如 JSON)

    AJAX 在推動(dòng) Web 2.0 的過程中發(fā)揮了重要的,比如產(chǎn)生了很多高度交互的服務(wù),如Google Maps、Writely等。基本上,它允許 JavaScript 發(fā)出HTTP 請(qǐng)求,獲取和處理結(jié)果,刷新局部頁面元素而不是整個(gè)頁面。在大多數(shù)瀏覽器請(qǐng)求的格式如下:

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

    REST

    SOAP 在創(chuàng)建自己的消息傳遞協(xié)議時(shí)是基于HTTP,但實(shí)際上 REST (REpresentational State Transfer) 的方式才是保持 Web 的原理和使用 HTTP 協(xié)議的核心部分。

    原始的 HTTP 協(xié)議已經(jīng)定義了四個(gè)命令,清晰地映射到各種數(shù)據(jù)(定義為“資源”)操作:

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

    REST 其背后的理念是使用這些 HTTP 命令來請(qǐng)求和操作數(shù)據(jù)。作為 HTTP協(xié)議的一部分,REST 使用 URL 來引用對(duì)象和操作。考慮這個(gè) HTTP 操作列表的例子:

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

    這個(gè)命令將返回一個(gè) XML 文檔,其中包含部分的列表。注意,返回的不是一個(gè)網(wǎng)頁,只是一個(gè)包含所請(qǐng)求的數(shù)據(jù) XML 數(shù)據(jù)結(jié)構(gòu)。

    <?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

    要特定部分的詳細(xì)信息,發(fā)送一個(gè)HTTP get 命令:

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

    這將返回一個(gè)特定的信息部分:

    <?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 的參數(shù)。例如:

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

    REST 不是 RPC,但也有類似的請(qǐng)求-響應(yīng)模式。制定透明度請(qǐng)求、封送數(shù)據(jù)、解析響應(yīng)這些不屬于 REST。REST 應(yīng)用非常廣泛,如 Yahoo! Search API、Ruby on Rails、Twiter 和 Open Zing Services 等。

    Google Protocol Buffers:封送處理

    有些時(shí)候,不僅僅是為了 RPC 和 Web Services 的需要,程序員只是想簡化對(duì)網(wǎng)絡(luò)上的數(shù)據(jù)的封送編組和解封的操作。Google Protocol Buffers 就是為序列化結(jié)構(gòu)化數(shù)據(jù)提供了一種有效的機(jī)制,使它容易對(duì)網(wǎng)絡(luò)上的數(shù)據(jù)進(jìn)行編碼和解碼。Protocol Buffers 是一個(gè)緊湊的二進(jìn)制格式比 XML 更簡單、體積更小、速度更快。他們是獨(dú)立于語言的,只定義數(shù)據(jù)類型。每個(gè)消息是對(duì)數(shù)據(jù)名稱、類型和值的結(jié)構(gòu)化集合。消息結(jié)構(gòu)定義在一個(gè)高級(jí)別的格式,類似于許多接口定義語言。然后文件可以根據(jù)你選擇的語言來編譯轉(zhuǎn)換成與該語言相應(yīng)的格式。Protocol Buffers 在 Google 中廣泛使用。目前已經(jīng)有超過48000種不同的消息類型定義。Protocol Buffers 可以被運(yùn)用在類 RPC 消息傳遞以及持久性存儲(chǔ)(將數(shù)據(jù)轉(zhuǎn)換成標(biāo)準(zhǔn)的串行形式寫入到一個(gè)文件中)。下面是一個(gè)定義 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

    請(qǐng)注意,這只定義了數(shù)據(jù)結(jié)構(gòu),而不是功能。使用這個(gè)結(jié)構(gòu)的例子是:

    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 在時(shí)間和空間方面,解析將更加有效。下面是兩者的對(duì)比.

    這個(gè)是 XML 格式:

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

    這個(gè)沒有編譯的 Protocol Buffers 格式:

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

    Protocol Buffers 產(chǎn)生的二進(jìn)制消息大約是28字節(jié)長,解析耗時(shí)大概需要100-200ns。相比之下,XML 版本需要69個(gè)字節(jié)長(是 Protocol Buffers 的 2.5倍),耗時(shí)是5000-10000ns(是 Protocol Buffers 的 50倍)。

    JSON

    JSON(JavaScript Object Notation) 是一種輕量級(jí)的數(shù)據(jù)交換格式。它基于 ECMAScript 的一個(gè)子集。JSON 采用完全獨(dú)立于語言的文本格式,但是也使用了類似于 C 語言家族的習(xí)慣(包括C、C++、C#、Java、JavaScript、Perl、Python 等)。這些特性使 JSON 成為理想的數(shù)據(jù)交換語言。易于人閱讀和編寫,同時(shí)也易于機(jī)器解析和生成。JSON 不是一個(gè)諸如 Google Protocol Buffers 的二進(jìn)制格式,因此適合使用基于 HTTP的消息傳遞。JSON 是可以作為 XML 替代品,在遠(yuǎn)程過程調(diào)用中,很多語言都支持 JSON-RPC。記住,這只是一個(gè)消息傳遞格式,JSON 并沒有試圖提供 RPC 庫來支持服務(wù)發(fā)現(xiàn)、綁定、托管和垃圾收集。

    參考引用

    • http://www.cs.virginia.edu/~zaher/classes/CS656/birrel.pdf
    • https://www.cs.rutgers.edu/~pxk/417/notes/03-rpc.html
    • http://queue.acm.org/detail.cfm?id=1142044
    • https://en.wikipedia.org/wiki/Web_Services_Description_Language
    • https://msdn.microsoft.com/en-us/library/ms973864.aspx
    • https://msdn.microsoft.com/en-us/library/bb985129.aspx

    轉(zhuǎn)載于:https://www.cnblogs.com/xxj-bigshow/p/9155162.html

    總結(jié)

    以上是生活随笔為你收集整理的RPC(Remote Procedure Calls)远程过程调用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。