文章轉(zhuǎn)載https://network.51cto.com/art/201906/598227.htm?mobile
RMI的定義
RPC (Remote Procedure Call):遠(yuǎn)程方法調(diào)用谷朝,用于一個(gè)進(jìn)程調(diào)用另一個(gè)進(jìn)程中的過程,從而提供了過程的分布能力武花。
RMI(Remote Method Invocation):遠(yuǎn)程方法調(diào)用圆凰,即在RPC的基礎(chǔ)上有向前邁進(jìn)了一步,提供分布式對象間的通訊体箕。允許運(yùn)行在一個(gè)java 虛擬機(jī)的對象調(diào)用運(yùn)行在另一個(gè)java虛擬機(jī)上對象的方法专钉。這兩個(gè)虛擬機(jī)可以是運(yùn)行在相同計(jì)算機(jī)上的不同進(jìn)程中,也可以是運(yùn)行在網(wǎng)絡(luò)上的不同計(jì)算機(jī)中累铅。
RMI的全稱宗旨就是盡量簡化遠(yuǎn)程接口對象的調(diào)用跃须。
RMI大大增強(qiáng)了java開發(fā)分布式應(yīng)用的能力,例如可以將計(jì)算方法復(fù)雜的程序放在其他的服務(wù)器上娃兽,主服務(wù)器只需要去調(diào)用菇民,而真正的運(yùn)算是在其他服務(wù)器上進(jìn)行,***將運(yùn)算結(jié)果返回給主服務(wù)器投储,這樣就減輕了主服務(wù)器的負(fù)擔(dān)第练,提高了效率(但是也有其他的開銷)。
RMI網(wǎng)絡(luò)模型
在設(shè)計(jì)初始階段玛荞,我們真正想要的是這樣一種機(jī)制复旬,客戶端程序員以常規(guī)方式進(jìn)行方法調(diào)用,而無需操心將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)上或者解析響應(yīng)之類的問題冲泥。所以才有了如下的網(wǎng)絡(luò)模型:在客戶端為遠(yuǎn)程對象安裝一個(gè)代理驹碍。代理是位于客戶端虛擬機(jī)中的一個(gè)對象壁涎,它對于客戶端程序來說,就像是要訪問的遠(yuǎn)程對象一樣志秃≌颍客戶端調(diào)用此代理時(shí),只需進(jìn)行常規(guī)的方法調(diào)用浮还。而客戶端代理則負(fù)責(zé)使用網(wǎng)絡(luò)協(xié)議與服務(wù)器進(jìn)行聯(lián)系竟坛。
<center style="color: rgb(51, 51, 51); font-family: "Microsoft Yahei"; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;"></center>
現(xiàn)在的問題在于代理之間是如何進(jìn)行通信的?通常有三種方法:
1钧舌、CORBA:通過對象請求代理架構(gòu)担汤,支持任何編程語言編寫的對象之間的方法調(diào)用。
2洼冻、SOAP
3崭歧、RMI:JAVA的遠(yuǎn)程方法調(diào)用技術(shù),支持java的分布式對象之間的方法調(diào)用撞牢。
其中CORBA與SOAP都是完全獨(dú)立于言語的率碾,可以使用C、C++屋彪、JAVA來編寫所宰,而RMI只適用于JAVA。
RMI的工作原理
一畜挥、術(shù)語介紹
1仔粥、存根:當(dāng)客戶端要調(diào)用遠(yuǎn)程對象的一個(gè)方法時(shí),實(shí)際上調(diào)用的是代理對象上的一個(gè)普通方法蟹但,我們稱此代理對象為存根(stub)件炉。存根位于客戶端機(jī)器上,而非服務(wù)器上矮湘。
2斟冕、參數(shù)編組:存根會(huì)將遠(yuǎn)程方法所需的參數(shù)打包成一組字節(jié),對參數(shù)編碼的過程就稱為參數(shù)編組缅阳。參數(shù)編組的目的是將參數(shù)轉(zhuǎn)換成適合在虛擬機(jī)之間進(jìn)行傳遞的格式磕蛇,在RMI協(xié)議中,對象是使用序列化機(jī)制進(jìn)行編碼的十办。
二秀撇、編程模型
為了介紹RMI的編程模型,我下面會(huì)編寫一個(gè)DEMO向族。遠(yuǎn)程對象表示的是一個(gè)倉庫呵燕,而客戶端程序向倉庫詢問某個(gè)產(chǎn)品的價(jià)格。
1件相、接口定義
遠(yuǎn)程對象的能力是由在客戶端和服務(wù)器之間共享的接口所表示的:
<center style="color: rgb(51, 51, 51); font-family: "Microsoft Yahei"; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;"></center>
遠(yuǎn)程對象的接口必須擴(kuò)展Remote接口氧苍,它位于java.rmi包中。接口中所有的方法必須聲明拋出RemoteException異常泛范。這是因?yàn)檫h(yuǎn)程方法總是存在失敗的可能让虐,所以java編程語言要求每一次遠(yuǎn)程方法的調(diào)用都必須捕獲RemoteException,并且指明當(dāng)調(diào)用不成功時(shí)應(yīng)執(zhí)行的相應(yīng)處理操作。
2罢荡、接口的實(shí)現(xiàn)
<center style="color: rgb(51, 51, 51); font-family: "Microsoft Yahei"; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;"></center>
你可以看出這個(gè)類是遠(yuǎn)程方法調(diào)用的目標(biāo),因?yàn)樗鼣U(kuò)展自UnicastRemoteObject区赵,這個(gè)類的構(gòu)造器使得它的對象可供遠(yuǎn)程訪問惭缰。
3、RMI注冊表:通過JNDI發(fā)布RMI服務(wù)
- 要訪問服務(wù)器上的一個(gè)遠(yuǎn)程對象時(shí)笼才,客戶端必須先得到一個(gè)本地的存根對象漱受,也就是客戶端機(jī)器上的代理對象。那么問題來了患整,如何才能得到這個(gè)存根呢?
- 為此拜效,JDK提供了自舉注冊服務(wù)(bootstrap registry service)喷众,服務(wù)器程序應(yīng)該使用自舉注冊服務(wù)來注冊至少一個(gè)遠(yuǎn)程對象各谚。
- 而要注冊一個(gè)遠(yuǎn)程對象,需要一個(gè)RMI URL和一個(gè)對實(shí)現(xiàn)對象的引用到千。
- RMI 的URL以rmi:開頭昌渤,后接域名或IP地址(host),緊接著是端口號(hào)(port)憔四,***是服務(wù)名(service)膀息。
如:rmi://regserver.mycompany.cmo:99/central_warehouse
如果我們是在本地發(fā)布RMI服務(wù),那么host就是“l(fā)ocalhost”了赵,此外RMI默認(rèn)的端口號(hào)是“1099”潜支,當(dāng)然我們也可以自行設(shè)置,只要不與其他端口重復(fù)即可柿汛。 service實(shí)際上是基于同一個(gè)host與port下唯一的服務(wù)名冗酿。
發(fā)布RMI服務(wù):
<center style="color: rgb(51, 51, 51); font-family: "Microsoft Yahei"; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;"></center>
運(yùn)行結(jié)果:
<pre style="padding: 0px; margin: 0px 0px 1em; font-family: "Courier New", monospace; font-size: 12px; width: 643.5px; overflow: auto; background: rgb(230, 230, 230); color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">
- Constructing server implementation
- Binding server implementation to registry
- Waiting for invocations from clients ...
</pre>
- 第20行只需提供一個(gè)port络断,就在JNDI中創(chuàng)建了一個(gè)注冊表裁替。
- 第21行通過bind方法綁定了RMI地址與RMI服務(wù)實(shí)現(xiàn)類。
- 執(zhí)行這個(gè)方法后貌笨,相當(dāng)于自動(dòng)發(fā)布了RMI服務(wù)弱判。接下來要做的事情就是寫一個(gè)RM客戶端調(diào)用已發(fā)布的RMI服務(wù)。
4锥惋、****調(diào)用RMI服務(wù)
<center style="color: rgb(51, 51, 51); font-family: "Microsoft Yahei"; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;"></center>
運(yùn)行結(jié)果:
<pre style="padding: 0px; margin: 0px 0px 1em; font-family: "Courier New", monospace; font-size: 12px; width: 643.5px; overflow: auto; background: rgb(230, 230, 230); color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">
- RMI registry binding:
- mate7:3700.0
</pre>
- 服務(wù)調(diào)用只需要知道兩個(gè)東西:1开伏、RMI請求路徑;2、RMI接口名
- 第15行剥哑,這里用的是接口名Warehouse,而不是實(shí)現(xiàn)類硅则。一定不能RMI接口的實(shí)現(xiàn)類,否則就是本地調(diào)用了株婴。
- 查看運(yùn)行結(jié)果怎虫,我們知道這次DEMO展示的遠(yuǎn)程調(diào)用成功了。
5困介、下面我們來看下RMI的網(wǎng)絡(luò)示意圖:
<center style="color: rgb(51, 51, 51); font-family: "Microsoft Yahei"; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;"></center>
- 借助JNDI這個(gè)所謂的命名與目錄服務(wù),我們成功地發(fā)布并調(diào)用了RMI服務(wù)座哩。實(shí)際上徒扶,JNDI就是一個(gè)注冊表,服務(wù)端將服務(wù)對象放入到注冊表中根穷,客戶端從注冊表中獲取服務(wù)對象姜骡。
- 在服務(wù)端我們發(fā)布了RMI服務(wù),并在JNDI中進(jìn)行了注冊屿良,此時(shí)就在服務(wù)端創(chuàng)建了一個(gè)Skeleton(骨架)圈澈,當(dāng)客戶端***次成功連接JNDI并獲取遠(yuǎn)程服務(wù)對象后,立馬在本地創(chuàng)建了一個(gè)Stub(存根)尘惧。
- 遠(yuǎn)程通信實(shí)際是通過Skeleton與Stub來完成的康栈,數(shù)據(jù)是基于TCP/IP協(xié)議,在“傳輸層”上發(fā)送的喷橙。
- 毋庸置疑啥么,理論上RMI一定比WebService要快,畢竟WebService是基于http協(xié)議的贰逾,而http所攜帶的數(shù)據(jù)是通過“應(yīng)用層”來傳輸?shù)男佟鬏攲虞^應(yīng)用層更為底層,越底層越快疙剑。
RMI的局限性
- 只能實(shí)現(xiàn)JAVA系統(tǒng)之間的調(diào)用氯迂,而WebService可以實(shí)現(xiàn)跨語言實(shí)現(xiàn)系統(tǒng)之間的調(diào)用。
- RMI使用了JAVA默認(rèn)的序列化方式核芽,對于性能要求比較高的系統(tǒng)囚戚,可能需要其他的序列化方案來解決。
- RMI服務(wù)在運(yùn)行時(shí)難免會(huì)存在故障轧简,例如驰坊,如果RMI服務(wù)無法連接了,就會(huì)導(dǎo)致客戶端無法響應(yīng)的現(xiàn)象哮独。