天天看點

WebService與RMI(遠端調用方式實作系統間通信)

前言

  本文是《分布式java應用基礎與實踐》讀書筆記;另外參考了此部落格,感覺講的挺好的,尤其是其中如下内容:

  另外,消息方式實作系統間通信本文不涉及。RMI則隻采用spring RMI架構來實作效果,更多的則是來講講webService及效果。

RMI

  (Remote Method Invocation) ----》 spring RMI(配置及實作直接參考spring文檔,已經很詳細了)

  spring RMI工作原理圖如下:

WebService與RMI(遠端調用方式實作系統間通信)

  RMI代碼結構圖:

  服務端代碼

    接口Business.java:

WebService與RMI(遠端調用方式實作系統間通信)
WebService與RMI(遠端調用方式實作系統間通信)
package com.rmi.server;

public interface Business {

    /**
     * 顯示用戶端提供的消息,并傳回
     * @param message
     * @return
     */
    public String echo(String message);
}      

View Code

    接口實作類BusinessImpl.java:

WebService與RMI(遠端調用方式實作系統間通信)
WebService與RMI(遠端調用方式實作系統間通信)
package com.rmi.server;

public class BusinessImpl implements Business {

    @Override
    public String echo(String message) {
        if("quit".equalsIgnoreCase(message.toString())){
            System.out.println("Server will be shutdown!");
            System.exit(0);;
        }
        
        System.out.println("Message from client:" + message);
        return "Server response:" + message;
    }

}      

    spring RMI配置檔案spring-server.xml:

WebService與RMI(遠端調用方式實作系統間通信)
WebService與RMI(遠端調用方式實作系統間通信)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="businessService" class="com.rmi.server.BusinessImpl"/>
    <bean class="org.springframework.remoting.rmi.RmiServiceExporter">
        <property name="service" ref="businessService"/>
        <property name="serviceName" value="BusinessService"/>                                <!-- 對外暴露的服務接口 -->
        <property name="serviceInterface" value="com.rmi.server.Business"/>
        <property name="registryPort" value="1199"/>                                        <!-- 端口号,預設是1099 -->
    </bean>
</beans>      

    服務啟動類:

WebService與RMI(遠端調用方式實作系統間通信)
WebService與RMI(遠端調用方式實作系統間通信)
package com.rmi.server;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ServerMain {

    public static void main(String[] args) throws Exception{
        
        new ClassPathXmlApplicationContext("classpath:spring-server.xml");
        System.out.println("Server has been started...");
    }

}      

  用戶端代碼

    配置檔案spring-client.xml:

WebService與RMI(遠端調用方式實作系統間通信)
WebService與RMI(遠端調用方式實作系統間通信)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="businessService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
        <property name="serviceUrl" value="rmi://192.168.1.101:1199/BusinessService"/> <!-- ip、端口以及接口不要寫錯了 -->
        <property name="serviceInterface" value="com.rmi.server.Business"/>
    </bean>
</beans>      

    用戶端調用服務java:

WebService與RMI(遠端調用方式實作系統間通信)
WebService與RMI(遠端調用方式實作系統間通信)
package com.rmi.client;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.rmi.server.Business;

public class ClientMain {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring-client.xml");
        Business bn = (Business) ac.getBean("businessService");
        System.out.println(bn.echo("hello"));
    }

}      

  測試結果

    先啟動服務端,再運作用戶端

調用成功!

  測試補充

    1.多了個可以選擇的ip

    在啟動服務的時候,注意了下輸出資訊,如下圖

     當時就納悶了,這個ip是哪來的,伺服器端(在Windows系統上)的ip不是192.168.1.101嗎,怎麼崩出來個192.168.56.1?,後來ipconfig下

    發現那個ip是安裝virtualbox後,路由配置設定給它的ip,測試發現,用戶端調用時的ip寫兩個中的任意一個都是可以的,都是能成功調用的!如果沒用虛拟機軟體的話是不存在這個現象的。

    是以大家寫的時候寫伺服器端的ip是肯定不會錯的!

    2.伺服器端與用戶端分離

      之前測試時,用戶端與伺服器端是在一台機器上的,通路能成功,那麼将兩者分開了?

      windows環境(192.168.1.101)做伺服器端,啟動個虛拟機做用戶端(192.168.1.111)做用戶端,将原來用戶端代碼移到111上(有些許差别,伺服器端的接口class檔案------Business接口 需要拷貝過去,不然用戶端代碼報錯)。

      測試結果怎麼都是連接配接逾時,無論用戶端調用的是192.168.1.101還是192.168.56.1。資料查了一些,沒能解決,希望知道的朋友可以在評論區留言,幫兄弟我解決此問題!

      因為重點在webService上,上面的問題我先放一放。

WebService

  概念

  大家可以去看看這篇部落格,個人感覺webService的相關概念解釋的挺清楚的。

  簡而言之:WebService是一種跨程式設計語言和跨作業系統平台的遠端調用技術。

  所謂跨程式設計語言和跨操作平台,就是說服務端程式采用java編寫,用戶端程式則可以采用其他程式設計語言編寫,反之亦然!跨作業系統平台則是指服務端程式和用戶端程式可以在不同的作業系統上運作。

      所謂遠端調用,就是一台計算機a上的一個程式可以調用到另外一台計算機b上的一個對象的方法,譬如,銀聯提供給商場的pos刷卡系統,商場 的POS機轉賬調用的轉賬方法的代碼其實是跑在銀行伺服器上。再比如,amazon,天氣預報系統,淘寶網,校内網,百度等把自己的系統服務以webservice服務的形式暴露出來,讓第三方網站和程式可以調用這些服務功能,這樣擴充了自己系統的市場占有率。

     其實可以從多個角度來了解WebService,從表面上看,WebService就是一個應用程式向外界暴露出一個能通過Web進行調用的 API,也就是說能用程式設計的方法通過Web來調用這個應用程式。我們把調用這個WebService的應用程式叫做用戶端,而把提供這個 WebService的應用程式叫做服務端。從深層次看,WebService是建立可互操作的分布式應用程式的新平台,是一個平台,是一套标準。它定義 了應用程式如何在Web上實作互操作性,你可以用任何你喜歡的語言,在任何你喜歡的平台上寫Web service ,隻要我們可以通過 Web service标準對這些服務進行查詢和通路。 

     WebService平台需要一套協定來實作分布式應用程式的建立。任何平台都有它的資料表示方法和類型系統。要實作互操作性,WebService平台 必須提供一套标準的類型系統,用于溝通不同平台、程式設計語言群組件模型中的不同類型系統。Web service平台必須提供一種标準來描述 Web service,讓客戶可以得到足夠的資訊來調用這個Web service。最後,我們還必須有一種方法來對這個Web service進行遠 程調用,這種方法實際是一種遠端過程調用協定(RPC)。為了達到互操作性,這種RPC協定還必須與平台和程式設計語言無關。

  要素

  XML和XSD:可擴充的标記語言是Web Service平台中表示資料的基本格式。除了易于建立和易于分析外,XML主要的優點在于它既與平台無關,又與廠商無關。

  WSDL:Web Service Description Language,網絡服務描述語言,描述服務所使用的協定、所期望的參數、傳回的參數格式等。

  SOAP:Simple Object Access Protocol,簡單對象通路協定;soap協定是基于HTTP協定的,soap也是基于XML和XSD的,XML是soap的資料編碼方式。打個比喻:HTTP就是普通公路,XML就是中間的綠色隔離帶和兩邊的防護欄,soap就是普通公路經過加隔離帶和防護欄改造過的高速公路。

  UDDI:Universal Description, Discovery, and Integration,是一套基于web的、分布式的、為web service提供的資訊注冊中心的實作标準規範;相當于是一個注冊中心,以便被客戶發現,更容易被找到。基本用不到,這裡隻是提一下。

  開發

    服務端開發:把公司内部系統的業務方法釋出成WebService服務,供遠端合作機關和個人調用。(借助一些WebService架構可以很輕松地把自己的業務對象釋出成WebService服務,Java方面的典型WebService架構包括:axis,xfire,cxf 等,java ee伺服器通常也支援釋出WebService服務,例如JBoss。)

    用戶端開發:調用别人釋出的WebService服務,大多數人從事的開發都屬于這個方面,例如,調用天氣預報 WebService服務。

    WebService的工作調用原理:對用戶端而言,我們給這各類WebService用戶端API傳遞wsdl檔案 的url位址,這些API就會建立出底層的代理類,我調用這些代理,就可以通路到webservice服務。代理類把用戶端的方法調用變成soap格式的請求資料再通過HTTP協定發出去,并把接收到的soap資料變成傳回值傳回。對服務端而言,各類WebService架構的本質就是一個大大的Servlet,當遠端調用用戶端給它通過http協定發送過來soap格式的請求資料時,它分析這個資料,就知道要調用哪個java類的哪個方法,于是去查找或建立這個對象,并調用其方法,再把方法傳回的結果包裝成soap格式的資料,通過http響應消息回給用戶端。

  實作

    基于CXF開源架構

    CXF工作原理圖

    代碼結構圖

    為了省事,cxf的lib下的所有jar檔案(不包括檔案夾和WHICH_JARS)全部導入到了工程中。

    服務端代碼

      接口HelloWorld.java

WebService與RMI(遠端調用方式實作系統間通信)
WebService與RMI(遠端調用方式實作系統間通信)
package demo.hw.server;

public interface HelloWorld {
    
    String sayHi(String name);

}      

      接口實作HelloWorldImpl.java

WebService與RMI(遠端調用方式實作系統間通信)
WebService與RMI(遠端調用方式實作系統間通信)
package demo.hw.server;

public class HelloWorldImpl implements HelloWorld {

    public String sayHi(String name) {
        System.out.println("sayHi called");
        return "Hello " + name;
    }
}      

      服務啟動Server.java

WebService與RMI(遠端調用方式實作系統間通信)
WebService與RMI(遠端調用方式實作系統間通信)
package demo.hw.server;

import org.apache.cxf.frontend.ServerFactoryBean;

public class Server {

    public Server() throws Exception{
        HelloWorldImpl hw = new HelloWorldImpl();
        ServerFactoryBean sfb = new ServerFactoryBean();
        sfb.setServiceClass(HelloWorld.class);
        sfb.setServiceBean(hw);
        sfb.setAddress("http://192.168.1.101:9000/Hello");
        sfb.create();
    }
     
    public static void main(String[] args) throws Exception{
        new Server();
        System.out.println("server start ...");
        Thread.sleep(5*60*1000);
        System.out.println("server exit ...");
        System.exit(0);
    }
}      

    用戶端代碼

      啟動Client.java

WebService與RMI(遠端調用方式實作系統間通信)
WebService與RMI(遠端調用方式實作系統間通信)
package demo.hw.client;

import org.apache.cxf.frontend.ClientProxyFactoryBean;
import demo.hw.server.HelloWorld;

public final class Client {

    public static void main(String[] args) {
        ClientProxyFactoryBean clientFactory = new ClientProxyFactoryBean();
        clientFactory.setAddress("http://192.168.1.101:9000/Hello");
        HelloWorld hw = clientFactory.create(HelloWorld.class);
        System.out.println(hw.sayHi("Hi"));
    }

}      

    啟動服務,如下圖

    調用服務,運作用戶端代碼,如下圖,服務調用成功!

    這裡測的時候,用戶端與服務端在一台機器上,那麼不在一台機器上是個什麼情況了?我們将用戶端移到虛拟機192.168.1.111上(服務端唉192.168.1.101上),如下圖

    結果顯示,測試成功!

總結

  一般而言,開源架構都會有詳細的文檔講解,大家用的時候最好去閱讀它的文檔,就好象spring rmi,它的文檔就将的挺詳細的,你去看的時候還會發現,spring也有他的webService實作;cxf沒有文檔,隻有api,但是他提供了很多的樣例,在samples檔案夾下,子檔案看名字就知道其下的樣例實作的是什麼功能,我的cxf示例就是參考的java_first_pojo檔案夾。老外寫的官方文檔其實還是挺好讀的!

  其他的一些開源架構像axis2等,我就沒去實踐了,大同小異,大家有時間可以去實踐下。

  另外我文中的那個問題,希望有夥伴幫我解答下!

  還是那句話:看的淺,試了才知深!

繼續閱讀