首先要了解一个概念: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 初步了解相关推荐

  1. JAVA RPC:从上手到爱不释手

    2019独角兽企业重金招聘Python工程师标准>>> 文首,思考一个问题:为什么需要 RPC 服务? 在传统的开发模式中,我们通常将系统的各个服务部署在单台机器,随着服务的扩展,这 ...

  2. 201671010135 2016--2017java程序设计对java的初步认识和对第一,二章的总结(0)

    201671010135  2016--2017<java程序设计>对java的初步认识和对第一,二章的总结(0) java是一种程序语言设计.html是一种描述网页结构的方式.除了用于在 ...

  3. 自己实现Java RPC框架一:RMI

    RPC 的主要作用是调用远程的服务,而且要快速. Java自带的RPC一种实现:RMI. 我们可以通过RMI快速实现一个RPC服务. 先定义一个接口,叫MyService: import java.r ...

  4. 实现java RPC框架

    http://javatar.iteye.com/blog/1123915 主要利用socket通信,反射,代理实现类似RMI的RPC框架 首先是框架的代码 package framework;imp ...

  5. java rpc与webservice_RPC体系,RPC和WebService的区别详解

    RPC和WebService的关系 RPC(Remote Procedure Call)- 远程过程调用,是一个很大的概念, 它是一种通过网络从远程计算机程序上跨语言跨平台的请求服务,rpc能省略部分 ...

  6. gRPC创建Java RPC服务

    1.说明 本文介绍使用gRPC创建Java版本的RPC服务, 包括通过.proto文件生成Java代码的方法, 以及服务端和客户端代码使用示例. 2.创建生成代码工程 创建Maven工程,grpc-c ...

  7. JAVA RPC (六) 之thrift反序列化RPC消息体

    我们来看一下服务端的简单实现,直接上thrift代码,很直观的来看一看thrift的server到底干了些什么 1 public boolean process(TProtocol in, TProt ...

  8. java rpc 框架 常用_常用的RPC架构系列---gRPC

    gRPC是谷歌的一个高性能,开源的高性能 RPC 框架,gRPC面向移动和HTTP/2设计.gRPC隐藏了底层的实现细节,包括序列化(json,xml),数据传输(TCP,HTTP,UDP),反序列化 ...

  9. java fragment_初步认识Fragment 之一 编写简单的fragment代码

    今天初步学习到了Fragment ,如果说activity是一堵墙, 那么它类似于一个便利贴,能将内容写在上面粘贴到任意一面墙上, 而不需要每一堵墙上都写满相同的内容 , 下面我们来了解 怎样编写 F ...

最新文章

  1. 深度学习多变量时间序列预测:Bi-LSTM算法构建时间序列多变量模型预测交通流量+代码实战
  2. linux elf 文件查看工具 readelf
  3. 皮一皮:到底谁才是渣王...
  4. 虚拟dom_通过编写简易虚拟DOM,来学习虚拟DOM 的原理
  5. jq获取img高度(动态生成的image高度为0原因)
  6. CMD是什么?(命令行提示符)如何使用python在windows上操作CMD?(python执行命令行)os.syste[m](执行的命令)、os.popen(执行的命令)
  7. sql server 判断是否存在数据库,表,列,视图
  8. OpenGL camera相机的实例
  9. C#进阶系列——WebApi 接口测试工具:WebApiTestClient
  10. EXECUTE IMMEDIATE用法小解
  11. Read the Docs 文档管理
  12. 服务器低功耗cpu性能,节能省电家庭共享 7款低功耗处理器推荐
  13. 20200426:186周周赛(上)(leetcode5392-5394)
  14. 关于注册测绘师的点点滴滴
  15. Msm8960(APQ8064)平台的MSM-AOSP-kitkat编译适配(6):音频
  16. 谨以此文,献给我的大学四年—双非本科到双一流985的成长之路
  17. 2021-06-10-APP PUSH推送机制
  18. 记2019北航计算机夏令营体验~
  19. C++的成员函数指针简要
  20. 详解回归分析中相关变量选择的方法和构建思想——岭回归、LASSO 回归、主成分回归和偏最小二乘回归

热门文章

  1. kill不掉 spark-submit
  2. SVC调参总结+调参实例
  3. linux检查运行程序文件,LINUX定时检查程序运行状态
  4. LINUX 下编译 ffmpeg
  5. 爬取json Swaggerui界面
  6. linux 下设置定时任务
  7. 建立http服务器,基于名称的虚拟主机
  8. eclipse中anroid adk添加
  9. SharePoint 2010 大中小架构的部署
  10. mvn tutorial