實驗1 遠程過程調(diào)用

實驗?zāi)康?/h2>
  • 利用RMI技術(shù)構(gòu)建遠程會話:2個客戶端發(fā)送消息到服務(wù)器端妥曲,服務(wù)器端把相應(yīng)的消息中轉(zhuǎn)發(fā)送給對應(yīng)的客戶贾费,進行遠程對話钦购。
  • 使用Dubbo框架實現(xiàn)此功能。

RMI實現(xiàn)

1.定義此服務(wù)的接口

public interface Message extends java.rmi.Remote{
    public List<String> getMessageById(int id) throws RemoteException;
    public void sendMessageById(String mess,int sourse,int destination) throws RemoteException;
}

2.定義服務(wù)器端的接口實現(xiàn)

public class MessageImpl implements Message,Serializable{
    
    //用一個hash表存取用戶的信息褂萧,key即用戶的id
    Map<Integer, List<String>> message=new HashMap<>();
    
    //獲取指定用戶的信息
    public List<String> getMessageById(int id) throws RemoteException
    {
        return message.get(id);
    }
    
    //某用戶給指定用戶發(fā)送消息
    public void sendMessageById(String mess,int sourse,int destination) throws RemoteException
    {
        if(message.get(destination)!=null)
        {
            List<String> old=message.get(destination);
            old.add(mess);
        } 
        else
        {
            List<String> messageList=new ArrayList<>();
            messageList.add(mess);
            message.put(destination, messageList);
        }
    }
}

3.構(gòu)建服務(wù)器:將服務(wù)綁定到指定端口(也可隨機)并發(fā)布押桃,同時開一個注冊表的端口,將服務(wù)添加到注冊表中导犹。
疑問:為啥服務(wù)端口可以和注冊端口一樣而不報錯唱凯?

public class Server {
    
    
    public static void main(String[] args) throws RemoteException, AlreadyBoundException {
        
        
        MessageImpl message=new MessageImpl();
        Message stub=(Message) UnicastRemoteObject.exportObject(message, 3333);//將服務(wù)綁定到端口3333上
        Registry registry=LocateRegistry.createRegistry(3333);//用端口3333開一個注冊表

        registry.bind("Message", stub);//將服務(wù)添加到注冊表
        System.out.println("bind success! listen on port 3333....");
        
        
        //查看注冊表中的服務(wù)
        for(int i=0;i<registry.list().length;i++)
        {
            System.out.println(registry.list()[i]);
        }   
        
    }
}

UnicastRemoteObject.exportObject(message, 3333)這個步驟很關(guān)鍵,它將此服務(wù)發(fā)布到3333端口上谎痢。只有通過這一步磕昼,才會生成遠程對象的存根,如果省去這一步直接把MessageImpl對象綁定到注冊表上节猿,當(dāng)客戶端調(diào)用此服務(wù)時得到的將不會是服務(wù)器端對象的存根票从,二是根據(jù)傳來的類信息在客戶端本地創(chuàng)建一個MessageImpl對象.如:
客戶端代碼:

String url="rmi://localhost:3333/Message";
        Message stub=(Message) Naming.lookup(url);
        System.out.println(stub.getClass());

服務(wù)器端不省去UnicastRemoteObject.exportObject(message, 3333)時漫雕,客戶端輸出:

class com.sun.proxy.$Proxy0

$Proxy0即為存根對象的類型

服務(wù)器端改寫成

MessageImpl message=new MessageImpl();
//Message stub=(Message) UnicastRemoteObject.exportObject(message, 3333);//將服務(wù)綁定到端口3333上
Registry registry=LocateRegistry.createRegistry(3333);//用端口3333開一個注冊表
registry.bind("Message", message);//將服務(wù)添加到注冊表

客戶端輸出:

4.編寫客戶端程序:定義兩個客戶,id為1,2峰鄙,分別隔1s,2s給對方發(fā)消息并查看自己的新消息浸间。
(只貼出client1的代碼)

public class Client1 {
    public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
        int index=0;
        String url="rmi://localhost:3333/Message";
        Message stub=(Message) Naming.lookup(url);
        while(true)
        {
            List<String> messageList=stub.getMessageById(1);
            if(messageList!=null)
            {
                while(index<messageList.size())
                {
                    System.out.println(messageList.get(index));
                    index++;
                }
            }
            stub.sendMessageById("hello,client2!", 1, 2);
            try{
                Thread.sleep(1000);
            }
            catch(Exception e){
                System.exit(0);//退出程序
            }
        }
    }
}

5.運行服務(wù)器后,同時運行客戶端1,2


server.png

client1.png

client2.png

注意:
服務(wù)端需要實現(xiàn)遠程接口吟榴,因為服務(wù)端需要具體的遠程對象綁定相應(yīng)的服務(wù)魁蒜。但是服務(wù)所提供的功能可以在客戶端實現(xiàn),而對于服務(wù)器卻是透明的吩翻。具體如下:

服務(wù)器提供功能的模板接口TFunction兜看,并將其作為遠程對象方法Compute的參數(shù),Compute進行TFunction方法的調(diào)用狭瞎。
客戶端用相應(yīng)的類Function實現(xiàn)接口TFunction铣减,同時實現(xiàn)Serializable接口。
基于對象串行化技術(shù)脚作,客戶端將實例Function1作為參數(shù)傳入葫哗,調(diào)用遠程對象的方法Compute武花。

這樣就使得客戶端將計算的類傳給服務(wù)器端河闰,讓服務(wù)端調(diào)用,實現(xiàn)了簡單的云計算服務(wù)押搪。
(參考o(jì)racle官網(wǎng)實例:https://docs.oracle.com/javase/tutorial/rmi/client.html)亿扁。

Dubbo框架實現(xiàn)

Dubbo架構(gòu)

dubbo框架.png

Dubbo注冊中心

對于服務(wù)提供方捺典,它需要發(fā)布服務(wù),而且由于應(yīng)用系統(tǒng)的復(fù)雜性从祝,服務(wù)的數(shù)量襟己、類型也不斷膨脹;
對于服務(wù)消費方牍陌,它最關(guān)心如何獲取到它所需要的服務(wù)擎浴,而面對復(fù)雜的應(yīng)用系統(tǒng),需要管理大量的服務(wù)調(diào)用毒涧。
而且贮预,對于服務(wù)提供方和服務(wù)消費方來說,他們還有可能兼具這兩種角色契讲,即既需要提供服務(wù)仿吞,有需要消費服務(wù)。

通過將服務(wù)統(tǒng)一管理起來捡偏,可以有效地優(yōu)化內(nèi)部應(yīng)用對服務(wù)發(fā)布/使用的流程和管理唤冈。服務(wù)注冊中心可以通過特定協(xié)議來完成服務(wù)對外的統(tǒng)一

Dubbo提供的注冊中心有如下幾種類型可供選擇:

  • Multicast注冊中心
  • Zookeeper注冊中心
  • Redis注冊中心
  • Simple注冊中心

本例采用Zookeeper注冊中心,為什么采用Zookeeper?

Zookeeper是一個分布式的服務(wù)框架,是樹型的目錄服務(wù)的數(shù)據(jù)存儲银伟,能做到集群管理數(shù)據(jù) 你虹,這里能很好的作為Dubbo服務(wù)的注冊中心凉当。
Dubbo能與Zookeeper做到集群部署,當(dāng)提供者出現(xiàn)斷電等異常停機時售葡,Zookeeper注冊中心能自動刪除提供者信息看杭,當(dāng)提供者重啟時,能自動恢復(fù)注冊數(shù)據(jù)挟伙,以及訂閱請求

安裝完成后楼雹,進入到bin目錄,并且啟動zkServer.cmd尖阔,這個腳本中會啟動一個java進程:
(注:需要先啟動zookeeper后贮缅,后續(xù)dubbo demo代碼運行才能使用zookeeper注冊中心的功能)


zookeeper.png

Dubbo優(yōu)勢

  • 透明化的遠程方法調(diào)用
    像調(diào)用本地方法一樣調(diào)用遠程方法;只需簡單配置介却,沒有任何API侵入谴供。
  • 軟負載均衡及容錯機制
    可在內(nèi)網(wǎng)替代nginx lvs等硬件負載均衡器。
  • 服務(wù)注冊中心自動注冊 & 配置管理
    不需要寫死服務(wù)提供者地址齿坷,注冊中心基于接口名自動查詢提供者ip桂肌。
    使用類似zookeeper等分布式協(xié)調(diào)服務(wù)作為服務(wù)注冊中心,可以將絕大部分項目配置移入zookeeper集群永淌。
  • 服務(wù)接口監(jiān)控與治理

實現(xiàn)流程

1.創(chuàng)建dubbo-api的MAVEN項目(有獨立的pom.xml崎场,用來打包供提供者消費者使用)。

public interface DemoService {
    List<String> getMessageById(int id);
    void sendMessageById(String mess,int sourse,int destination);
}

2.創(chuàng)建dubbo-provider的MAVEN項目(有獨立的pom.xml遂蛀,用來打包供消費者使用)谭跨。
實現(xiàn)服務(wù)接口

public class DemoServiceImpl implements DemoService {
    Map<Integer, List<String>> message=new HashMap<>();

    public List<String> getMessageById(int id)
    {
        return message.get(id);
    }

    public void sendMessageById(String mess,int sourse,int destination)
    {
        if(message.get(destination)!=null)
        {
            List<String> old=message.get(destination);
            old.add(mess);
        }
        else
        {
            List<String> messageList=new ArrayList<>();
            messageList.add(mess);
            message.put(destination, messageList);
        }
    }
}

配置聲明暴露服務(wù),將服務(wù)注冊到Zookeeper上

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--定義了提供方應(yīng)用信息李滴,用于計算依賴關(guān)系螃宙;在 dubbo-admin 或 dubbo-monitor 會顯示這個名字,方便辨識-->
    <dubbo:application name="demotest-provider" owner="programmer" organization="dubbox"/>
    <!--使用 zookeeper 注冊中心暴露服務(wù)所坯,注意要先開啟 zookeeper-->
    <dubbo:registry address="zookeeper://localhost:2181"/>
    <!-- 用dubbo協(xié)議在20880端口暴露服務(wù) -->
    <dubbo:protocol name="dubbo" port="20880" />
    <!--使用 dubbo 協(xié)議實現(xiàn)定義好的 api.PermissionService 接口-->
    <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" protocol="dubbo" />
    <!--具體實現(xiàn)該接口的 bean-->
    <bean id="demoService" class="com.alibaba.dubbo.demo.impl.DemoServiceImpl"/>
</beans>

provider開啟服務(wù)

public class Provider {
        public static void main(String[] args) throws IOException {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("provider.xml");
            System.out.println(context.getDisplayName() + ": here");
            context.start();
            System.out.println("服務(wù)已經(jīng)啟動...");
            System.in.read();
        }
    }

server.png

3.創(chuàng)建dubbo-consumer的MAVEN項目
通過Spring配置引用遠程服務(wù)

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <dubbo:application name="demotest-consumer" owner="programmer" organization="dubbox"/>
    <!--向 zookeeper 訂閱 provider 的地址谆扎,由 zookeeper 定時推送-->
    <dubbo:registry address="zookeeper://localhost:2181"/>
    <dubbo:reference id="messageService" interface="com.alibaba.dubbo.demo.DemoService"/>
</beans>

啟動Consumer,調(diào)用遠程服務(wù)

public class Consumer {
    public static void main(String[] args) {
        //測試常規(guī)服務(wù)
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("consumer.xml");
        context.start();
        System.out.println("consumer start");
        DemoService demoService = context.getBean(DemoService.class);
        System.out.println("consumer");
        int index=0;
        while(true)
        {
            List<String> messageList=demoService.getMessageById(1);
            if(messageList!=null)
            {
                while(index<messageList.size())
                {
                    System.out.println(messageList.get(index));
                    index++;
                }
            }
            demoService.sendMessageById("hello,client2!", 1, 2);
            try{
                Thread.sleep(1000);
            }
            catch(Exception e){
                System.exit(0);//退出程序
            }
        }
    }
}

4.同時運行consumer1,consumer2


consumer1.png

consumer2.png

參考博文https://blog.csdn.net/noaman_wgs/article/details/70214612

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市包竹,隨后出現(xiàn)的幾起案子燕酷,更是在濱河造成了極大的恐慌籍凝,老刑警劉巖周瞎,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異饵蒂,居然都是意外死亡声诸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門退盯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彼乌,“玉大人泻肯,你說我怎么就攤上這事∥空眨” “怎么了灶挟?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長毒租。 經(jīng)常有香客問我稚铣,道長,這世上最難降的妖魔是什么墅垮? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任惕医,我火速辦了婚禮,結(jié)果婚禮上算色,老公的妹妹穿的比我還像新娘抬伺。我一直安慰自己,他們只是感情好灾梦,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布峡钓。 她就那樣靜靜地躺著,像睡著了一般若河。 火紅的嫁衣襯著肌膚如雪椒楣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天牡肉,我揣著相機與錄音捧灰,去河邊找鬼。 笑死统锤,一個胖子當(dāng)著我的面吹牛毛俏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播饲窿,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼煌寇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了逾雄?” 一聲冷哼從身側(cè)響起阀溶,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸦泳,沒想到半個月后银锻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡做鹰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年击纬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钾麸。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡更振,死狀恐怖炕桨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肯腕,我是刑警寧澤献宫,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站实撒,受9級特大地震影響遵蚜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜奈惑,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一吭净、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肴甸,春花似錦寂殉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至庶柿,卻和暖如春村怪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浮庐。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工甚负, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人审残。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓梭域,卻偏偏與公主長得像,于是被迫代替她去往敵國和親搅轿。 傳聞我的和親對象是個殘疾皇子病涨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355