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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java RPC 初步了解

發布時間:2023/12/19 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java RPC 初步了解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先要了解一個概念:wsdl 協議 web service description language

使用wsdl 要定義一個接口,一個服務;目前常用的就是xml 描述,類似java中 jax-ws?

WSDL 元素? 基于XML語法描述了與服務進行交互的基本元素:
Type(消息類型):數據類型定義的容器,它使用某種類型系統(如 XSD)。
Message(消息):通信數據的抽象類型化定義,它由一個或者多個 part 組成。
Part:消息參數
Operation(操作):對服務所支持的操作進行抽象描述,WSDL定義了四種操作: 1.單向(one-way):端點接受信息;2.請求-響應(request-response):端點接受消息,然后發送相關消息;3.要求-響應(solicit-response):端點發送消息,然后接受相關消息;4.通知(notification ):端點發送消息。
Port Type(端口類型):特定端口類型的具體協議和數據格式規范。
Binding:特定端口類型的具體協議和數據格式規范。
Port:定義為綁定和網絡地址組合的單個端點。
Service:相關端口的集合,包括其關聯的接口、操作、消息等。

-------------------------------------------------------------------------------------------------------

JAX-WS(Java API for XML Web Services)規范是一組XML web services的JAVA API,JAX-WS允許開發者可以選擇RPC-oriented或者message-oriented 來實現自己的web services。

在 JAX-WS中,一個遠程調用可以轉換為一個基于XML的協議例如SOAP,在使用JAX-WS過程中,開發者不需要編寫任何生成和處理SOAP消息的代碼。JAX-WS的運行時實現會將這些API的調用轉換成為對應的SOAP消息。 在服務器端,用戶只需要通過Java語言定義遠程調用所需要實現的接口SEI(service endpoint interface),并提供相關的實現,通過調用JAX-WS的服務發布接口就可以將其發布為WebService接口。 在客戶端,用戶可以通過JAX-WS的API創建一個代理(用本地對象來替代遠程的服務)來實現對于遠程服務器端的調用。 當然 JAX-WS 也提供了一組針對底層消息進行操作的API調用,你可以通過Dispatch 直接使用SOAP消息或XML消息發送請求或者使用Provider處理SOAP或XML消息。 通過web service所提供的互操作環境,我們可以用JAX-WS輕松實現JAVA平臺與其他編程環境(.net等)的互操作。 JAX-WS與JAX-RPC之間的關系 Sun最開始的web services的實現是JAX-RPC 1.1 (JSR 101)。這個實現是基于Java的RPC,并不完全支持schema規范,同時沒有對Binding和Parsing定義標準的實現。 JAX-WS2.0 (JSR 224)是Sun新的web services協議棧,是一個完全基于標準的實現。在binding層,使用的是the Java Architecture for XML Binding (JAXB, JSR 222),在parsing層,使用的是the Streaming API for XML (StAX, JSR 173),同時它還完全支持schema規范。

SOAP 協議:簡單對象訪問協議? simple object access protocol.

一個 SOAP 實例

在下面的例子中,一個 GetStockPrice 請求被發送到了服務器。此請求有一個 StockName 參數,而在響應中則會返回一個 Price 參數。此功能的命名空間被定義在此地址中: "http://www.example.org/stock"

SOAP 請求:

POST /InStock HTTP/1.1
Host: www.example.org
Content-Type: application/soap+xml; charset=utf-8
Content-Length: nnn

<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">

<soap:Body xmlns:m="http://www.example.org/stock">
? <m:GetStockPrice>
??? <m:StockName>IBM</m:StockName>
? </m:GetStockPrice>
</soap:Body>

</soap:Envelope>

SOAP 響應:

HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset=utf-8
Content-Length: nnn

<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">

<soap:Body xmlns:m="http://www.example.org/stock">
? <m:GetStockPriceResponse>
??? <m:Price>34.5</m:Price>
? </m:GetStockPriceResponse>
</soap:Body>

</soap:Envelope>


使用JAX-WS(JWS)發布WebService,實現輕量級WebService框架--demo

我們使用JAX-WS開發WebService只需要很簡單的幾個步驟:寫接口和實現=>發布=>生成客戶端(測試或使用)。

而在開發階段我們也不需要導入外部jar包,因為這些api都是現成的。首先是接口的編寫(接口中只需要把類注明為@WebService,把 要暴露給客戶端的方法注明為@WebMethod即可,其余如@WebResult、@WebParam等都不是必要的,而客戶端和服務端的通信用RPC 和Message-Oriented兩種,區別和配置以后再說):

package service;import java.util.Date;import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.jws.soap.SOAPBinding;/*** 作為測試的WebService接口* * @author Johness* */ @WebService @SOAPBinding(style = SOAPBinding.Style.RPC) public interface SayHiService {/*** 執行測試的WebService方法*/@WebMethodvoid SayHiDefault();/*** 執行測試的WebService方法(有參)* * @param name*/@WebMethodvoid SayHi(@WebParam(name = "name") String name);/*** 執行測試的WebService方法(用于時間校驗)* * @param clentTime 客戶端時間* @return 0表示時間校驗失敗 1表示校驗成功*/@WebMethod@WebResult(name = "valid")int CheckTime(@WebParam(name = "clientTime") Date clientTime); } package service.imp;import java.text.SimpleDateFormat; import java.util.Date;import javax.jws.WebService; import javax.jws.soap.SOAPBinding;import service.SayHiService;/*** 作為測試的WebService實現類* * @author Johness* */ @WebService(endpointInterface = "service.SayHiService") @SOAPBinding(style = SOAPBinding.Style.RPC) public class SayHiServiceImp implements SayHiService {@Overridepublic void SayHiDefault() {System.out.println("Hi, Johness!");}@Overridepublic void SayHi(String name) {System.out.println("Hi, " + name + "!");}@Overridepublic int CheckTime(Date clientTime) {// 精確到秒String dateServer = new java.sql.Date(System.currentTimeMillis()).toString()+ " "+ new java.sql.Time(System.currentTimeMillis());String dateClient = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(clientTime);return dateServer.equals(dateClient) ? 1 : 0;}}

然后是發布(一般有兩種方式):

方式一(此方式只能作為調試,有以下bug:

jdk1.6u17?以下編譯器不支持以Endpoint.publish方式發布document方式的soap,必須在service接口和實現類添加“@SOAPBinding(style = SOAPBinding.Style.RPC)”注解;訪問受限,似乎只能本機訪問(應該會綁定到publish的URL上,如下使用localhost的話就只能本機訪問)……):

package mian;import javax.xml.ws.Endpoint;import service.imp.SayHiServiceImp;public class Main {/*** 發布WebService* 簡單*/public static void main(String[] args) {Endpoint.publish("http://localhost:8080/testjws/service/sayHi", new SayHiServiceImp());}}

方式二(基于web服務器Servlet方式):

以Tomcat為例,首先編寫sun-jaxws.xml文件并放到WEB-INF下:

<?xml version="1.0" encoding="UTF-8"?> <endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"version="2.0"><endpoint name="SayHiService"implementation="service.imp.SayHiServiceImpl"url-pattern="/service/sayHi" /> </endpoints>

然后改動web.xml,添加listener和servlet(url-pattern要相同):

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"><listener> <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener </listener-class></listener><servlet><servlet-name>SayHiService</servlet-name> <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet </servlet-class></servlet> <servlet-mapping> <servlet-name>SayHiService</servlet-name> <url-pattern>/service/sayHi</url-pattern> </servlet-mapping><welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file></welcome-file-list> </web-app>

最后部署到Tomcat里,值得一提的是您可能需要添加以下jar包(因為Tomcat沒有):


 啟動Tomcat。

服務端工作就完成了,注意兩個事情。

  注意:項目需要使用UTF-8編碼(至少sun-jaxws.xml必須是UTF-8格式的);

  對于MyEclipse的內置Tomcat,可能會出現不需要手動添加上述jar包,但獨立部署時應該添加,因為它們使用的class-path不一樣;

  多個不同路徑的接口也要使用同一個WSServlet;

  最好加上@SOAPBinding(style = SOAPBinding.Style.RPC)注解。

  部署好了之后打開瀏覽器輸入網址:http://localhost:8080/testjws/service/sayHi?wsdl。可以看到東西就證明發布成功了。

  附上項目樹狀圖:

最后是客戶端使用,由于WebService是平臺和語言無關的基于xml的,所以我們完全可以使用不同語言來編寫或生成客戶端。

一般有三種方式來使用(對于Java語言而言):

一,使用jdk自帶工具wsimport生成客戶端:

jdk自帶的wsimport工具生成,上圖我是把客戶端文件生成到了桌面src文件中(-d),并保留了源文件(-keep),指定了包名(-p)。

然后我們就可以使用生成的文件來調用服務器暴露的方法了:


值得一提的是你生成使用的jdk和你客戶端的jre需要配套!

從上面的目錄結構我們可以發現:服務端的每個webmethod都被單獨解析成為了一個類(如果使用了實體,實體也會被解析到客戶端,并且是源碼,所以建議使用實體時慎重)。


(上面的圖是舊圖,只是為了表示一下jaxws是為每個webmethod生成類的情況)

而我們的service則被生成了一個代理類來調用服務,接下來我們看看使用情況:

package test;import java.util.Date; import java.util.GregorianCalendar;import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar;import testjws.client.SayHiService; import testjws.client.SayHiServiceImpService;public class Main {public static void main(String[] args) throws DatatypeConfigurationException {// 獲取serviceSayHiService service = new SayHiServiceImpService().getSayHiServiceImpPort();// sayhiservice.sayHiDefault();service.sayHi("Ahe");// checktime// 這里主要說一下時間日期的xml傳遞,方法還略顯復雜GregorianCalendar calender = new GregorianCalendar();calender.setTime(new Date(System.currentTimeMillis()));XMLGregorianCalendar xmldate = DatatypeFactory.newInstance().newXMLGregorianCalendar(calender);System.out.println(service.checkTime(xmldate));}}

看看服務器的輸出,我們是否調用成功:


成功了!

對于校驗時間的方法客戶端也收到反饋了:


二,使用諸如MyEclipse(Eclipse for Jave EE也可以)創建一個Web Service Client的項目


然后填入wsdl地址即可,后續步驟我就不貼出了。

-----------------------------------------------------------------------------------------------------------

以java rmi? 方式實現rpc


RMI遠程調用步驟:

1,客戶對象調用客戶端輔助對象上的方法

2,客戶端輔助對象打包調用信息(變量,方法名),通過網絡發送給服務端輔助對象

3,服務端輔助對象將客戶端輔助對象發送來的信息解包,找出真正被調用的方法以及該方法所在對象

4,調用真正服務對象上的真正方法,并將結果返回給服務端輔助對象

5,服務端輔助對象將結果打包,發送給客戶端輔助對象

6,客戶端輔助對象將返回值解包,返回給客戶對象

7,客戶對象獲得返回值

對于客戶對象來說,步驟2-6是完全透明的

1、創建遠程方法接口,該接口必須繼承自Remote接口

Remote 接口是一個標識接口,用于標識所包含的方法可以從非本地虛擬機上調用的接口,Remote接口本身不包含任何方法

package server; import java.rmi.Remote; import java.rmi.RemoteException; public interface Hello extends Remote { public String sayHello(String name) throws RemoteException; }

由于遠程方法調用的本質依然是網絡通信,只不過隱藏了底層實現,網絡通信是經常會出現異常的,所以接口的所有方法都必須拋出RemoteException以說明該方法是有風險的

2、創建遠程方法接口實現類:

UnicastRemoteObject類的構造函數拋出了RemoteException,故其繼承類不能使用默認構造函數,繼承類的構造函數必須也拋出RemoteException

由于方法參數返回值最終都將在網絡上傳輸,故必須是可序列化的

package server; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class HelloImpl extends UnicastRemoteObject implements Hello { private static final long serialVersionUID = -271947229644133464L; public HelloImpl() throws RemoteException{ super(); } public String sayHello(String name) throws RemoteException { return "Hello,"+name; } } 3、利用java自帶rmic工具生成sutb存根類(jdk1.5.0_15/bin/rmic)

jdk1.2以后的RMI可以通過反射API可以直接將請求發送給真實類,所以不需要skeleton類了

sutb存根為遠程方法類在本地的代理,是在服務端代碼的基礎上生成的,需要HelloImpl.class文件,由于HelloImpl繼承了Hello接口,故Hello.class文件也是不可少的

Test

- - server

- - - - Hello.class

- - - - HelloImpl.class

方式一:

[name@name Test]$ cd /home/name/Test/ [name@name Test]$ rmic server.HelloImpl

方式二:

[name@name Test]$ rmic -classpath /home/name/Test server.HelloImpl

?運行成功后將會生成HelloImpl_Stub.class文件

4、啟動RMI注冊服務(jdk1.5.0_15/bin/rmiregistry)

方式一:后臺啟動rmiregistry服務

[name@name jdk]$ jdk1.5.0_15/bin/rmiregistry 12312 & [1] 22720 [name@name jdk]$ ps -ef|grep rmiregistry name 22720 13763 0 16:43 pts/3 00:00:00 jdk1.5.0_15/bin/rmiregistry 12312 name 22737 13763 0 16:43 pts/3 00:00:00 grep rmiregistry

如果不帶具體端口號,則默認為1099

方式二:人工創建rmiregistry服務,需要在代碼中添加:

LocateRegistry.createRegistry(12312); 5、編寫服務端代碼
package server; import java.rmi.Naming; import java.rmi.registry.LocateRegistry; public class HelloServer { public static void main(String[] args) { try{ Hello h = new HelloImpl(); //創建并導出接受指定port請求的本地主機上的Registry實例。 //LocateRegistry.createRegistry(12312); /** Naming 類提供在對象注冊表中存儲和獲得遠程對遠程對象引用的方法 * Naming 類的每個方法都可將某個名稱作為其一個參數, * 該名稱是使用以下形式的 URL 格式(沒有 scheme 組件)的 java.lang.String: * //host:port/name * host:注冊表所在的主機(遠程或本地),省略則默認為本地主機 * port:是注冊表接受調用的端口號,省略則默認為1099,RMI注冊表registry使用的著名端口 * name:是未經注冊表解釋的簡單字符串 */ //Naming.bind("//host:port/name", h); Naming.bind("rmi://192.168.58.164:12312/Hello", h); System.out.println("HelloServer啟動成功"); }catch(Exception e){ e.printStackTrace(); } } }

先創建注冊表,然后才能在注冊表中存儲遠程對象信息

6、運行服務端(58.164):

Test

- - server

- - - - Hello.class

- - - - HelloImpl.class

- - - - HelloServer.class

[name@name ~]$ java server.HelloServer HelloServer啟動成功

當然/home/name/Test一定要在系統CLASSPATH中,否則會報找不到相應的.class文件

7、編寫客戶端代碼

package client; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; import server.Hello; public class HelloClient { public static void main(String[] args) { try { Hello h = (Hello)Naming.lookup("rmi://192.168.58.164:12312/Hello"); System.out.println(h.sayHello("zx")); } catch (MalformedURLException e) { System.out.println("url格式異常"); } catch (RemoteException e) { System.out.println("創建對象異常"); e.printStackTrace(); } catch (NotBoundException e) { System.out.println("對象未綁定"); } }

8、運行客戶端(58.163):

Test

- - client

- - - - HelloClient.class

- - server

- - - - Hello.class

- - - - HelloImpl_Stub.class//服務端生成的存根文件

[name@name client]$ java client.HelloClient Hello,zx

同服務器端,/home/name/Test一定要在系統CLASSPATH中


PS:

1、客戶端所在服務和服務端所在的服務器網絡一定要通(一開始浪費了很多時間,最后才發現是網絡不通)

2、所有代碼在jdk1.5.0_15,Linux服務器上調試通過

3、如果java命令運行提示找不到類文件,則為CLASSPATH配置問題

[name@name ~]$ vi .bash_profile JAVA_HOME=/home/name/jdk/jdk1.5.0_15 export JAVA_HOME PATH=$JAVA_HOME/bin:$PATH export PATH CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:/home/name/Test export CLASSPATH

JAVA_HOME為jdk的根目錄

PATH為java工具類路徑(java,javac,rmic等)

CLASSPATH為java?.class文件的存放路徑,使用java命令運行.class文件時即會在該參數配置的路徑下尋找相應文件


java RMI的缺點:

1、從代碼中也可以看到,代碼依賴于ip與端口

2、RMI依賴于Java遠程消息交換協議JRMP(Java Remote Messaging Protocol),該協議為java定制,要求服務端與客戶端都為java編寫






總結

以上是生活随笔為你收集整理的java RPC 初步了解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。