代理模式——遠(yuǎn)程代理(Java RMI)

代理模式——遠(yuǎn)程代理(Java RMI)

@(設(shè)計(jì)模式)

一楷力、遠(yuǎn)程代理——大使

在日常開(kāi)發(fā)中喊式,我們經(jīng)常會(huì)有本地服務(wù)完成不了的功能,需要訪(fǎng)問(wèn)遠(yuǎn)程服務(wù)中的對(duì)象(以下稱(chēng)遠(yuǎn)程對(duì)象)萧朝。當(dāng)然岔留,不可能用下面的代碼來(lái)引用遠(yuǎn)程對(duì)象,以獲取其方法:

Ref ref = <另一個(gè)JVM堆的對(duì)象>
1. 從實(shí)踐中歸納

從非面向?qū)ο蠼嵌燃旒恚梢悦枋鰹楸镜胤?wù)通過(guò)網(wǎng)絡(luò)請(qǐng)求遠(yuǎn)程服務(wù)献联。為了實(shí)現(xiàn)本地到遠(yuǎn)程的通信,我們需要實(shí)現(xiàn)網(wǎng)絡(luò)通信何址,處理其中可能的異常里逆。為良好的代碼設(shè)計(jì)和可維護(hù)性,我們將網(wǎng)絡(luò)通信部分隱藏起來(lái)用爪,只暴露給本地服務(wù)一個(gè)接口原押,通過(guò)該接口即可訪(fǎng)問(wèn)遠(yuǎn)程服務(wù)提供的功能,而不必過(guò)多關(guān)心通信部分的細(xì)節(jié)偎血。正如世上本沒(méi)有設(shè)計(jì)模式诸衔,設(shè)計(jì)模式都是從實(shí)踐中總結(jié)出來(lái)的良好設(shè)計(jì)方式,我們也將以上的總結(jié)為遠(yuǎn)程代理颇玷。最后笨农,我們良好設(shè)計(jì)的通信方式可能如下:

良好設(shè)計(jì)的通信方式

通信時(shí)序如下:

遠(yuǎn)程通信時(shí)序圖
2. 面向?qū)ο笏伎?/h5>

從非面向?qū)ο蠼嵌龋覀冴P(guān)注本地服務(wù)如何獲得遠(yuǎn)程服務(wù)的功能帖渠,而從面相對(duì)象角度谒亦,我們關(guān)注本地服務(wù)如何獲得遠(yuǎn)程對(duì)象,從而獲得遠(yuǎn)程服務(wù)提供的功能阿弃。這樣诊霹,本地對(duì)象具有與遠(yuǎn)程對(duì)象完全相同的行為,但是本地的行為是通過(guò)遠(yuǎn)程對(duì)象提供的渣淳,細(xì)細(xì)思考脾还,這就是代理模式了。不同的是代理類(lèi)和真實(shí)主題類(lèi)不在同一個(gè)JVM堆中入愧,但其類(lèi)之間的關(guān)系還是與代理模式相同的:

代理模式
3.大使

遠(yuǎn)程代理為一個(gè)位于不同的地址空間的對(duì)象提供一個(gè)局域代表對(duì)象鄙漏,這個(gè)不同的地址空間可以是在本機(jī)器中嗤谚,也可以是在另一臺(tái)機(jī)器中,遠(yuǎn)程代理的角色怔蚌,就像一個(gè)國(guó)家在另一個(gè)國(guó)家的大使一樣巩步,重大問(wèn)題還是要跟國(guó)內(nèi)溝通。

二桦踊、Java RMI實(shí)現(xiàn)遠(yuǎn)程代理

現(xiàn)在椅野,實(shí)現(xiàn)遠(yuǎn)程代理,我們需要實(shí)現(xiàn)網(wǎng)絡(luò)通信籍胯,封裝細(xì)節(jié)竟闪,處理異常,程序員都是懶惰而聰明的杖狼,Java已經(jīng)提供了一套成熟的方案來(lái)實(shí)現(xiàn)遠(yuǎn)程代理炼蛤。

利用Java RMI實(shí)現(xiàn)遠(yuǎn)程代理,需要分別實(shí)現(xiàn)客戶(hù)端與服務(wù)端

1. 服務(wù)端

實(shí)現(xiàn)遠(yuǎn)程代理服務(wù)端蝶涩,需要

  1. 制作遠(yuǎn)程接口理朋,即主題接口
    遠(yuǎn)程接口需要實(shí)現(xiàn)java.rmi.Remote類(lèi),該標(biāo)記接口表示其方法可能從非本地虛擬機(jī)調(diào)用绿聘;
    遠(yuǎn)程接口所有方法需聲明java.rmi.RemoteException異常嗽上,涉及網(wǎng)絡(luò)通信,方法調(diào)用可能產(chǎn)生異常斜友;
    遠(yuǎn)程接口方法變量返回值都是原語(yǔ)或者可序列化類(lèi)型炸裆,因?yàn)榉祷刂岛妥兞靠赡苄枰h(yuǎn)程傳輸。
  2. 實(shí)現(xiàn)遠(yuǎn)程接口鲜屏,即真正主題類(lèi)
    繼承java.rmi.server.UnicastRemoteObject類(lèi)烹看,該類(lèi)提供遠(yuǎn)程對(duì)象
  3. 啟動(dòng)本地RMI registry
    可以使用JDK自帶的rmiregistry命令啟動(dòng),也可以使用java.rmi.registry.LocateRegistry提供的createRegistry方法啟動(dòng)
  4. 注冊(cè)遠(yuǎn)程服務(wù)
    使用JNDI注冊(cè)服務(wù)

最后遠(yuǎn)程服務(wù)的類(lèi)圖如下:

遠(yuǎn)程服務(wù)

代碼清單

// 遠(yuǎn)程接口
public interface IMyRemote extends Remote {

    String sayHello() throws RemoteException;
}
// 遠(yuǎn)程接口實(shí)現(xiàn)-遠(yuǎn)程對(duì)象
public class MyRemoteImpl extends UnicastRemoteObject implements IMyRemote {

    public MyRemoteImpl() throws RemoteException {
        super();
    }

    @Override
    public String sayHello() throws RemoteException {
        return "Server says, 'Hey'";
    }

    public static void main(String[] args) throws RemoteException, MalformedURLException {
        IMyRemote service = new MyRemoteImpl();
        // 啟動(dòng)本地rmi registry洛史,默認(rèn)端口1099
        LocateRegistry.createRegistry(1099);
        // 注冊(cè)遠(yuǎn)程對(duì)象
        Naming.rebind("rmi://localhost:1099/RemoteHello", service);
    }
}
2.客戶(hù)端

對(duì)于客戶(hù)端來(lái)說(shuō)惯殊,只需要使用JNDI服務(wù)找到遠(yuǎn)程對(duì)象,使用即可也殖。代碼如下:

public class MyRemoteClient {

    private void go() throws RemoteException, NotBoundException, MalformedURLException {
        IMyRemote service = (IMyRemote) Naming.lookup("rmi://localhost/RemoteHello");
        System.out.println(service.sayHello());
    }

    public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException {
        new MyRemoteClient().go();
    }
}

首先啟動(dòng)服務(wù)端土思,再啟動(dòng)客戶(hù)端,就可以看到來(lái)自遠(yuǎn)程對(duì)象的問(wèn)候了忆嗜。

三己儒、深入Java RMI

Java RMI固然為我們實(shí)現(xiàn)遠(yuǎn)程對(duì)象提供了簡(jiǎn)便的方式,深入了解其實(shí)現(xiàn)細(xì)節(jié)也會(huì)讓我們受益匪淺捆毫。

1. 一切從通信開(kāi)始

不管怎么封裝闪湾,本地服務(wù)要想獲得遠(yuǎn)程對(duì)象,調(diào)用遠(yuǎn)程服務(wù)的方法绩卤,必然要經(jīng)過(guò)網(wǎng)絡(luò)通信途样。Java RMI為我們處理了這些細(xì)節(jié)江醇,現(xiàn)在我們要還原這些細(xì)節(jié)一窺RMI實(shí)現(xiàn)。一般地何暇,其中涉及了socket編程與TCP通信:

  1. 服務(wù)端監(jiān)聽(tīng)某個(gè)端口
  2. 客戶(hù)端知道服務(wù)端地址陶夜,并向服務(wù)端監(jiān)聽(tīng)的端口發(fā)請(qǐng)求

這樣的通訊方式引入了一些問(wèn)題:

  1. 誰(shuí)處理服務(wù)端socket相關(guān)細(xì)節(jié)
  2. 誰(shuí)處理客戶(hù)端socket相關(guān)細(xì)節(jié)
  3. 客戶(hù)端如何知道服務(wù)端地址,以及服務(wù)端監(jiān)聽(tīng)哪一個(gè)端口

為解決以上三個(gè)問(wèn)題裆站,RMI引入如下概念

  1. 誰(shuí)處理服務(wù)端socket相關(guān)細(xì)節(jié) ——skeleton
    骨架skelton接收來(lái)自存根的TCP請(qǐng)求条辟,隱藏通信細(xì)節(jié),將真正的調(diào)用交給遠(yuǎn)程對(duì)象宏胯,并負(fù)責(zé)組織返回捂贿,解耦服務(wù)與網(wǎng)絡(luò)通信;
  2. 誰(shuí)處理客戶(hù)端socket相關(guān)細(xì)節(jié) ——stub
    存根stub接收來(lái)自客戶(hù)的請(qǐng)求胳嘲,組織參數(shù)通過(guò)TCP發(fā)送到服務(wù)端骨架,并接收組織返回給客戶(hù)扣草,解耦客戶(hù)與網(wǎng)絡(luò)通信了牛;
  3. 客戶(hù)端如何知道服務(wù)端地址,以及服務(wù)端監(jiān)聽(tīng)哪一個(gè)端口 ——rmi registry
    服務(wù)端遠(yuǎn)程對(duì)象注冊(cè)到rmi registry辰妙,客戶(hù)端通過(guò)位置總所周知的rmi registry獲取存根stub鹰祸。rmi registry是一個(gè)獨(dú)立的服務(wù),只有在registry服務(wù)啟動(dòng)后密浑,才能將遠(yuǎn)程服務(wù)注冊(cè)(綁定)到registry中蛙婴。

這樣,遠(yuǎn)程對(duì)象的全部時(shí)序細(xì)節(jié)應(yīng)該如下尔破,其中Program是服務(wù)端負(fù)責(zé)啟動(dòng)的類(lèi)街图,RMI registry與服務(wù)端在同一個(gè)JVM中。

遠(yuǎn)程對(duì)象通信
2. 代碼細(xì)節(jié)

// TODO: java rmi的代碼細(xì)節(jié)

四懒构、分布式與RMI

rmi registry是一個(gè)不同于rmi服務(wù)端的獨(dú)立服務(wù)(這也是要先啟動(dòng)rmi registry才能綁定rmi遠(yuǎn)程服務(wù)的原因)餐济。由于將rmi服務(wù)綁定到RMI registry時(shí),registry使用自己的類(lèi)加載器加載服務(wù)類(lèi)胆剧,因此registry必須能夠訪(fǎng)問(wèn)到服務(wù)類(lèi)的classpath絮姆,那么registry必須和rmi服務(wù)在同一臺(tái)機(jī)器上了。這就意味這rmi 服務(wù)不能注冊(cè)到遠(yuǎn)程機(jī)器的rmi registry上秩霍,不能依靠rmi registry解決分布式中rmi服務(wù)的單點(diǎn)故障篙悯。

成熟的方案是集成zookeeper,將分布式部署的rmi服務(wù)的rmi registry地址注冊(cè)到zk某個(gè)節(jié)點(diǎn)铃绒,rmi客戶(hù)端訪(fǎng)問(wèn)該節(jié)點(diǎn)獲取rmi registry地址鸽照,采用合適的負(fù)載策略選擇具體rmi registry,最后通過(guò)rmi registry訪(fǎng)問(wèn)到具體rmi服務(wù)匿垄。那么整個(gè)過(guò)程如下:

rmi registry -> zk node -> rmi registry list -> Load Balancing Strategy -> specific rmi registry -> rmi server

refer to

[1] 深入理解rmi原理
[2] Java RMI遠(yuǎn)程方法調(diào)用詳解
[3] RMI注冊(cè)表
[4] RMI(遠(yuǎn)程方法調(diào)用)
[5] 使用 RMI + ZooKeeper 實(shí)現(xiàn)遠(yuǎn)程調(diào)用框架

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末移宅,一起剝皮案震驚了整個(gè)濱河市归粉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌漏峰,老刑警劉巖糠悼,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異浅乔,居然都是意外死亡倔喂,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)靖苇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)席噩,“玉大人,你說(shuō)我怎么就攤上這事贤壁〉渴啵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵脾拆,是天一觀(guān)的道長(zhǎng)馒索。 經(jīng)常有香客問(wèn)我,道長(zhǎng)名船,這世上最難降的妖魔是什么绰上? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮渠驼,結(jié)果婚禮上蜈块,老公的妹妹穿的比我還像新娘。我一直安慰自己迷扇,他們只是感情好百揭,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著谋梭,像睡著了一般信峻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瓮床,一...
    開(kāi)封第一講書(shū)人閱讀 49,837評(píng)論 1 290
  • 那天盹舞,我揣著相機(jī)與錄音,去河邊找鬼隘庄。 笑死踢步,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的丑掺。 我是一名探鬼主播获印,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼街州!你這毒婦竟也來(lái)了兼丰?” 一聲冷哼從身側(cè)響起玻孟,我...
    開(kāi)封第一講書(shū)人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鳍征,沒(méi)想到半個(gè)月后黍翎,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡艳丛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年匣掸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氮双。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碰酝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出戴差,到底是詐尸還是另有隱情送爸,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布暖释,位于F島的核電站碱璃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏饭入。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一肛真、第九天 我趴在偏房一處隱蔽的房頂上張望谐丢。 院中可真熱鬧,春花似錦蚓让、人聲如沸乾忱。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)窄瘟。三九已至,卻和暖如春趟卸,著一層夾襖步出監(jiān)牢的瞬間蹄葱,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工锄列, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留图云,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓邻邮,卻偏偏與公主長(zhǎng)得像竣况,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子筒严,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理丹泉,服務(wù)發(fā)現(xiàn)情萤,斷路器,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • 目錄 本文的結(jié)構(gòu)如下: 引言 什么是代理模式 模式的結(jié)構(gòu) 典型代碼 代理模式分類(lèi) 代碼示例 代理模式和裝飾者模式的...
    w1992wishes閱讀 1,526評(píng)論 0 13
  • 代理模式是什么 如上圖所示摹恨,代理代表著另一終端中的某個(gè)真實(shí)服務(wù)對(duì)象筋岛,Client 調(diào)用代理(Client help...
    野生西瓜閱讀 2,300評(píng)論 2 14
  • 今早揩晴,跑完五公里時(shí)勋陪,第15天晨跑,在木凳上坐了好一會(huì)兒硫兰。心里沒(méi)有想象中的那么激動(dòng)诅愚。起初,在手帳上記錄著?五公里劫映,要...
    夢(mèng)在南方9899閱讀 682評(píng)論 0 2
  • 一违孝、用Vue改造React的一個(gè)Demo Game 2048 https://github.com/pengfu/...
    pengfoo閱讀 442評(píng)論 0 0