精华内容
下载资源
问答
  • RPC和WebService的区别

    2020-08-06 15:10:29
    远程过程调用,是一个很大的概念, 它是一种通过网络从远程计算机程序上跨语言跨平台的请求服务,rpc能省略部分接口代码的开发,可以跨机器之间访问对象(java rmi),可以有更方便的加密更高效的数据传输性能,...

    1、RPC服务

    RPC(Remote Procedure Call)— 远程过程调用,是一个很大的概念, 它是一种通过网络从远程计算机程序上跨语言跨平台的请求服务,rpc能省略部分接口代码的开发,可以跨机器之间访问对象(java rmi),可以有更方便的加密和更高效的数据传输性能, 而不需要了解底层网络技术的协议, RPC不仅可以走HTTP/HTTPS, 也可以自定义 tcp 协议, 从而省略HTTP繁杂的规则和冗余信息。

    (1)RPC架构

    先说说RPC服务的基本架构吧。一个完整的RPC架构里面包含了四个核心的组件,分别是Client ,Server,Client Stub以及Server Stub,这个Stub大家可以理解为存根。分别说说这几个组件:

    1)客户端(Client),服务的调用方。

    2)服务端(Server),真正的服务提供者。

    3)客户端存根,存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。

    4)服务端存根,接收客户端发送过来的消息,将消息解包,并调用本地的方法。

      

    RPC主要是用在大型企业里面,因为大型企业里面系统繁多,业务线复杂,而且效率优势非常重要的一块,这个时候RPC的优势就比较明显了。实际的开发当中是这么做的,项目一般使用maven来管理。比如我们有一个处理订单的系统服务,先声明它的所有的接口(这里就是具体指Java中的interface),然后将整个项目打包为一个jar包,服务端这边引入这个二方库,然后实现相应的功能,客户端这边也只需要引入这个二方库即可调用了。为什么这么做?主要是为了减少客户端这边的jar包大小,因为每一次打包发布的时候,jar包太多总是会影响效率。另外也是将客户端和服务端解耦,提高代码的可移植性。

    (2)同步调用与异步调用

    什么是同步调用?什么是异步调用?同步调用就是客户端等待调用执行完成并返回结果。异步调用就是客户端不等待调用执行完成返回结果,不过依然可以通过回调函数等接收到返回结果的通知。如果客户端并不关心结果,则可以变成一个单向的调用。这个过程有点类似于Java中的callable和runnable接口,我们进行异步执行的时候,如果需要知道执行的结果,就可以使用callable接口,并且可以通过Future类获取到异步执行的结果信息。如果不关心执行的结果,直接使用runnable接口就可以了,因为它不返回结果,当然啦,callable也是可以的,我们不去获取Future就可以了。

    (3)流行的RPC框架

        目前流行的开源RPC框架还是比较多的。下面重点介绍三种:
        1)gRPC是Google最近公布的开源软件,基于最新的HTTP2.0协议,并支持常见的众多编程语言。 我们知道HTTP2.0是基于二进制的HTTP协议升级版本,目前各大浏览器都在快马加鞭的加以支持。 这个RPC框架是基于HTTP协议实现的,底层使用到了Netty框架的支持。
        2)Thrift是Facebook的一个开源项目,主要是一个跨语言的服务开发框架。它有一个代码生成器来对它所定义的IDL定义文件自动生成服务代码框架。用户只要在其之前进行二次开发就行,对于底层的RPC通讯等都是透明的。不过这个对于用户来说的话需要学习特定领域语言这个特性,还是有一定成本的。

        3)Dubbo是阿里集团开源的一个极为出名的RPC框架,在很多互联网公司和企业应用中广泛使用。协议和序列化框架都可以插拔是及其鲜明的特色。同样 的远程接口是基于Java Interface,并且依托于spring框架方便开发。可以方便的打包成单一文件,独立进程运行,和现在的微服务概念一致。

    2、WebService

    WebService是一种跨编程语言和跨操作系统平台的远程调用技术。

    一个应用程序向外界暴露出一个能通过Web进行调用的API,也就是说能用编程的方法通过 Web来调用这个应用程序。我们把调用这个WebService的应用程序叫做客户端,而把提供这个WebService的应用程序叫做服务端。从深层次 看,WebService是建立可互操作的分布式应用程序的新平台,是一个平台,是一套标准。它定义了应用程序如何在Web上实现互操作性,你可以用任何 你喜欢的语言,在任何你喜欢的平台上写Web service ,只要我们可以通过Web service标准对这些服务进行查询和访问。

    WebService接口实际上就是RPC调用的一种实现,只考虑RPC的stub层实现, soap restfull都是固定走HTTP/HTTPS, 都有定义了自己的规则和实现(xmll和json等), 规定了server能够提供的服务(web service),也是跨语言跨平台的

    RPC 与 WebService 图解:

    可以看到RPC模型中多了一个stub的组件,这个是约定的接口,也就是server提供的服务, web service就是基于这一层考虑了实现的, 至于调用的协议都是固定的, 注意这里的“接口”,不是指JAVA中的interface,因为RPC是跨平台跨语言的,用JAVA写的客户端,应该能够调用用C语言提供的过程。

     

    展开全文
  • 浅析RPCWebService

    万次阅读 多人点赞 2018-08-15 17:52:06
    虽然现在非常火的RPC技术以SpringCloudDubbo(x)为主流,但是如果做接口调用,还是逃不了要用一些较传统的技术。前几天在做接口调用时恰巧用到了WebService的相关技术(8,9两节是真实的开发),正好都在这里写一写...

    虽然现在非常火的RPC技术以SpringCloud和Dubbo(x)为主流,但是如果做接口调用,还是逃不了要用一些较传统的技术。前几天在做接口调用时恰巧用到了WebService的相关技术(8,9两节是真实的开发),正好都在这里写一写。

    1. RPC相关基础

    1.1 什么是RPC

    ----| RPC(Remote Procedure Call),远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。(来自百度百科)

    ----| RPC允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不需要显式编码这个远程调用的细节。(来自CSDN博客:https://blog.csdn.net/mindfloating/article/details/39473807)

    1.2 RPC的特点

    从上面的概念中,能大概总结出RPC的基本特点如下:

    • 通过网络传输的
    • 跨终端、跨平台的
    • 基于请求-响应的
    • 只调用过程,不需关注细节

    1.3 RPC的基本原理

    在底层去看,RPC其实就是将流从一台计算机传输到另外一台计算机,无论是基于传输协议(http、tcp、udp等等)和网络IO(bio、nio)来实现。

    1.4 常见的RPC的调用技术

    • SpringCloud(Spring的,基于Socket的,SOA架构的分布式框架)
    • Dubbo(x)(阿里巴巴的,基于Socket的,SOA架构的分布式框架)
    • WebService(跨语言的,基于SOAP协议,走xml数据或json数据)
    • Hessian(跨语言的,基于Binary-RPC协议,走二进制数据)
    • HttpClient(通常用于RESTful风格的调用,跨语言,基于http和json)
    • jdk原生(HttpURLConnection)

    参考资料:https://www.cnblogs.com/cainiao-Shun666/p/9181903.html

    由于本文主要介绍WebService,对其他的调用技术不展开介绍,有需要的小伙伴可以在博客平台上获取相关博客和资料。

     

    2. WebService是什么

    Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。(来自百度百科)

    简单的来讲:WebService是一种跨语言和跨操作系统的远程调用技术。

    有篇博客里讲的一段话不错,摘抄下来供小伙伴阅读:

    其实可以从多个角度来理解 WebService,从表面上看,WebService就是一个应用程序向外界暴露出一个能通过Web进行调用的API,也就是说能用编程的方法通过Web来调用这个应用程序。我们把调用这个WebService的应用程序叫做客户端,而把提供这个WebService的应用程序叫做服务端。从深层次看,WebService是建立可互操作的分布式应用程序的新平台,是一个平台,是一套标准。它定义了应用程序如何在Web上实现互操作性,你可以用任何你喜欢的语言,在任何你喜欢的平台上写WebService,只要我们可以通过WebService标准对这些服务进行查询和访问。(来自博客:https://www.cnblogs.com/xdp-gacl/p/4048937.html

    3. 为什么要用WebService

    • 平台语言无关性——WebService基于SOAP协议,且发布的所有服务都有对应的xml(wsdl),可以实现跨平台、跨语言的支持
    • 安全通信——WebService走http请求,不受防火墙限制
    • 功能复用——通过使用WebService,可以暴露共用服务,实现功能复用

    实际上一个WebService可以看做一个独立的功能,供外界使用。

    4. WebService的核心

    4.1 SOAP

    • SOAP(Simple Object Access Protocal),即简单对象访问协议,它用来描述传递信息的格式。
    • SOAP是一种简单的基于xml的协议和规范,它使应用程序通过http来交换信息。
    • SOAP可以简单理解为http + xml。

    其实,WebService通过http协议发送请求和接收结果时,发送的请求内容和结果内容都采用xml格式封装,并增加了一些特定的http消息头,以说明 http消息的内容格式,这些特定的http消息头和xml内容格式就是SOAP协议。

    4.2 WSDL

    • WSDL(Web Services Description Language),即网络服务描述语言,可描述WebService服务。
    • 一句话概括:WSDL是基于xml的用于描述WebService及其方法、参数和返回值的“说明书”。
    • WSDL设计成xml是有章可循的:xml可以很容易被DOM解析,标签及属性也容易被人阅读,进而看懂相应服务的使用方式。
    • 每一个WebService服务必定会有它对应的WSDL,通过这个WSDL,可以在不同语言平台上解析成对应语言的源码,进而被开发者使用。

    4.3 UDDI

    UDDI 的目的是为电子商务建立标准;UDDI是一套基于Web的、分布式的、为Web Service提供的、信息注册中心的实现标准规范,同时也包含一组使企业能将自身提供的Web Service注册,以使别的企业能够发现的访问协议的实现标准。

    5. 什么时候可以用WebService

    • 提供统一服务访问接口(没有语言限制)
    • 系统间服务发布与调用
    • 分布式(可以用但不方便,当然现在用的更多的是SpringCloud和Dubbo(x))
    • 对性能要求不是很高的服务

    6. 在Java中有什么方式使用WebService

    • jdk原生的WebService方式(基础)
    • 使用Apache的CXF框架(常用)
    • 使用Apache的axis框架(多语言,重量级)
    • 使用XFire框架(已没落)

    本文将使用原生的JWS和Apache的CXF框架来分别介绍如何使用WebService

    7. 怎么发布WebService服务

    为了与后面的CXF框架使用同一工程,这里直接用Maven搭建Provider-Demo工程

    7.1 创建服务提供方的Maven工程

    创建服务提供方的Maven工程,因为工程要使用Spring与CXF整合,所以打包方式为war

     

    7.2 创建基于JWS的WebService服务

    7.2.1 创建JWSProvider类,并加上@WebService注解

    import javax.jws.WebService;
    
    /**
     * 基于JWS的WebService服务提供者
     * @Title JWSProvider
     * @author LinkedBear
     */
    @WebService
    public class JWSProvider {
       
    }

    7.2.2 编写服务源码

    import javax.jws.WebService;
    import javax.xml.ws.Endpoint;
    
    /**
     * 基于JWS的WebService服务提供者
     * @Title JWSProvider
     * @author LinkedBear
     */
    @WebService
    public class JWSProvider {
        /**
         * 服务功能效果:在传入的名后追加一个随机数
         * @param name
         * @return
         */
        public String getRandomCode(String name) {
            System.out.println("基于JWS的WebService服务:getRandomCode被调用了。。。");
            return name + Math.random();
        }
        
        public static void main(String[] args) throws Exception {
            System.out.println("开始发布WebService服务。。。");
            Endpoint.publish("http://localhost/getRandomCode", new JWSProvider());
            System.out.println("WebService服务发布成功。。。");
        }
    }
    

    7.2.3 执行main方法,控制台打印成功提示启动基于JWS的服务

    浏览器输入http://localhost/getRandomCode?wsdl后,可以看到该服务的WSDL说明书,服务发布成功。

    7.3 创建基于CXF的WebService服务

    7.3.1 在pom.xml中加入CXF的依赖

    <properties>
    	<spring.version>5.0.4.RELEASE</spring.version>
    	<cxf.version>3.2.6</cxf.version>
    </properties>
    
    <dependencies>
        <!-- 导入CXF的依赖 -->
    	<dependency>
    		<groupId>org.apache.cxf</groupId>
    		<artifactId>cxf-core</artifactId>
    		<version>${cxf.version}</version>
    	</dependency>
    	<dependency>
    		<groupId>org.apache.cxf</groupId>
    		<artifactId>cxf-rt-frontend-jaxws</artifactId>
    		<version>${cxf.version}</version>
    	</dependency>
    	<dependency>
    		<groupId>org.apache.cxf</groupId>
    		<artifactId>cxf-rt-transports-http</artifactId>
    		<version>${cxf.version}</version>
    	</dependency>
    	<dependency>
    		<groupId>org.apache.cxf</groupId>
    		<artifactId>cxf-rt-transports-http-jetty</artifactId>
    		<version>${cxf.version}</version>
    	</dependency>
    
    	<!--导入Spring的依赖 -->
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-core</artifactId>
    		<version>${spring.version}</version>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-beans</artifactId>
    		<version>${spring.version}</version>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-context</artifactId>
    		<version>${spring.version}</version>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-expression</artifactId>
    		<version>${spring.version}</version>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-context-support</artifactId>
    		<version>${spring.version}</version>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-webmvc</artifactId>
    		<version>${spring.version}</version>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-aspects</artifactId>
    		<version>${spring.version}</version>
    	</dependency>
    </dependencies>
    

    7.3.2 在web.xml中配置CXF的Servlet

    <!-- CXF的Servlet -->
    <servlet>
        <servlet-name>cxf</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <init-param>
            <param-name>config-location</param-name>
            <param-value>classpath:applicationContext-cxf.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>cxf</servlet-name>
        <url-pattern>/webservice/*</url-pattern>
    </servlet-mapping>
    

    7.3.3 在src/main-resource目录下创建applicationContext-cxf.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    	xmlns:jaxws="http://cxf.apache.org/jaxws"
    	xmlns:soap="http://cxf.apache.org/bindings/soap"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans 
                            http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://cxf.apache.org/bindings/soap 
                            http://cxf.apache.org/schemas/configuration/soap.xsd
                            http://cxf.apache.org/jaxws 
                            http://cxf.apache.org/schemas/jaxws.xsd">
    	<!-- 导入jar包中的cxf配置文件 -->
    	<import resource="classpath:META-INF/cxf/cxf.xml" />
    	<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
    
    </beans>
    

    7.3.4 创建服务提供者接口和实现类

    注意@WebService注解要加到接口上!

    import javax.jws.WebService;
    
    /**
     * 基于CXF的服务提供者接口
     * @Title CXFProvider
     * @author LinkedBear
     */
    @WebService
    public interface CXFProvider {
        String getNameHashCode(String name);
    }
    

    实现类:

    /**
     * 基于CXF的服务提供者
     * @Title CXFProvider
     * @author LinkedBear
     */
    public class CXFProviderImpl implements CXFProvider {
        /**
         * 服务功能效果:在传入的name后追加hashCode
         * @param name
         * @return
         */
        @Override
        public String getNameHashCode(String name) {
            System.out.println("基于CXF的WebService服务:getNameHashCode被调用了。。。");
            return name + name.hashCode();
        }
    }
    

    7.3.5 在applicationContext-cxf中注册服务

    <!-- 注册服务的对象 -->
    <bean id="cxfProvider" class="com.linkedbear.webservice.cxf.CXFProviderImpl"/>
    <!-- 注册服务 -->
    <jaxws:server id="nameHashCodeServer" address="/nameHashCodeServer">
        <jaxws:serviceBean>
            <ref bean="cxfProvider"/>
        </jaxws:serviceBean>
    </jaxws:server>
    

    7.3.6 将工程部署到Tomcat,启动服务器

    注意!如果Spring的依赖导不全,启动时会报错!

    浏览器输入http://localhost:8080/WebService-Provider-Demo/webservice/nameHashCodeServer?wsdl后,可以看到该服务的WSDL说明书,服务发布成功。

    8. 怎么调用WebService服务

    先创建服务调用方的Maven工程。

    由于调用方只负责使用服务,故打包方式为jar即可。

    同样要导入CXF和Spring的依赖。

     

    服务发布好后,下一步就是如何调用服务。

    下面将分3部分介绍WebService服务的调用方式

    8.1 使用jdk自带的wsimport命令,生成本地源码,基于JWS调用

    在jdk安装目录下,找到bin目录,除了我们日常熟悉的javac,javap,javadoc等常用命令之外,还有一个wsimport命令:

    使用该命令,可以将指定的WSDL转化为本地源码+编译后的字节码文件。

    8.1.1 wsimport使用方式

    常用的命令参数如下:

    • -d <directory>  在指定的目录生成class文件
    • -clientjar <jarfile>  在当前目录生成jar文件,结合-d <directory>可以在指定的目录生成jar文件
    • -s <directory>  在指定的目录生成java源文件
    • -p <pkg>  指定生成文件的包结构
    • -keep  在生成class文件,或者jar包时,同时保留java源文件

    最常用的命令行语句为:

    wsimport –s . <wsdl>

    此处的.指的是生成源码的位置为当前文件夹

     

    我们将刚刚写好的基于JMS的WebService服务的WSDL取出,用wsimport命令生成本地源码,效果如下:

    http://localhost/getRandomCode?wsdl

    8.1.2 将解析的.java复制到工程中

    8.1.3 使用JMS的方式调用服务

    从生成的源码中可以看出,GetRandomCode.java为服务的调用类,要想使用服务,必定会使用到它。

    但如果你想直接创建这个类的对象去调用,那你只会扑一场空:

    这个类怎么没有对应的方法呢?

    既然你觉得这个类是我们发布的服务名,那它是不是可以类比于Java反射中的Method类呢?

    那既然Method类是表示一个方法的,那它肯定要有执行对象才可以调用该方法吧!

     

    正确调用方式:

    public class JWSConsumer {
        public static void main(String[] args) throws Exception {
            //先创建WebService服务对象
            JWSProviderService webservice = new JWSProviderService();
            //再创建该服务的提供者对象
            JWSProvider provider = webservice.getJWSProviderPort();
            //之后,才能调用服务
            String code = provider.getRandomCode("LinkedBear");
            System.out.println(code);
        }
    }

    8.2 使用CXF框架的方式调用服务

    使用CXF框架,在生成本地源码时要使用CXF自己的方式生成。

    8.2.1 wsdl2java使用方式

    CXF提供的解析命令为wsdl2java。

    我们在一开始使用wsimport时,即便不进入jdk的目录也能正常使用,是因为我们配置了PATH的环境变量。

    如果想在任意位置使用CXF的wsdl2java命令,也需要配置环境变量。但考虑到这个命令用的频率实在太低,故不配置,直接进入到CXF的目录下。

    从官网下载CXF的框架包,解压之后,可以从bin目录下找到wsdl2java.bat。

    wsdl2java命令的常用命令参数如下:

    • -p  指定生成的客户端包名
    • -d  指定生成的客户端生成目录
    • --compile  指定需要进行编译
    • -c  指定编译生成的目录
    • -client  指定生成客户端调用类, 即包含main方法调用客户端方法的类

     

    执行wsdl2java命令,可以将WSDL转换成.java(没有.class)

    8.2.2 将解析的.java复制到工程中

    我们只需要唯一的那个接口就可以了,不需要全部的源文件。

    但是删除其他源码后,接口会报错,原因是接口上有一个@XmlSeeAlso注解,需要传入ObjectFactory的class对象,这里并不需要这个Factory,直接删掉即可,只保留一对空的花括号。

    8.2.3 将服务注入到本地

    之所以在服务调用方也用到了Spring,是因为CXF与Spring配合时,可以只留下接口文件,利用Spring的服务注入,来进行远程调用。

    在src/main/resource目录下,创建applicationContext.xml文件,将CXF的服务注入到Spring的容器中(原理是Spring会创建这个接口的代理对象,通过代理对象去远程调用服务端的接口)。

    <jaxws:client id="consumer" 
                    address="http://localhost:8080/WebService-Provider-Demo/webservice/nameHashCodeServer" 
                    serviceClass="com.linkedbear.webservice.cxf.CXFProvider"/>
    

    8.2.4 使用CXF的方式调用服务

    public class App {
        public static void main(String[] args) throws Exception {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
            CXFProvider provider = (CXFProvider) ctx.getBean("consumer");
            String nameHashCode = provider.getNameHashCode("LinkedBear");
            System.out.println(nameHashCode);
        }
    }

    8.3 不生成本地源码的方式调用服务

    前面两种方式,最大的缺点是需要生成本地源码,费时费力不说,万一服务端改了接口的入参规则,你这边也要重新生成,再修改业务代码。

    那我们就想:能不能有一种方式,可以不生成本地源码的前提下,也能调用WebService服务呢?

    可以!使用HttpClient,配合SOAP协议,可以实现不生成本地源码的前提下,也能调用WebService服务。

    8.3.1 为什么使用HttpClient也可以成功调用服务呢?

    我们说,WebService是基于SOAP协议的,我们使用本地源码发送的请求,其实也就是这些基于SOAP的POST请求,收到的响应也是基于SOAP的响应。

    那么,如果我们自己构造基于SOAP协议的POST请求,是不是服务也就可以正常返回结果呢?当然是肯定的!

    不过,唯一不太好的是:自行构造源码,获得响应后需要自行解析响应体。

    8.3.2 导入HttpClient的依赖

    使用HttpClient,就需要导入HttpClient的依赖(注意不要导错了)。

    <dependency>
    	<groupId>commons-httpclient</groupId>
    	<artifactId>commons-httpclient</artifactId>
    	<version>3.1</version>
    </dependency>
    

    8.3.3 SOAP协议的请求体xml格式(精简)

    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
        <soap:Body>
            <[method] xmlns="[namaspace]">
                <[args]>[text]</[args]>
            </[method]>
        </soap:Body>
    </soap:Envelope>
    

    上面的格式中,方括号内的标识为具体WebService的请求。

    举个简单的栗子吧:

    url为http://localhost/getRandomCode?wsdl

    里面的namespace为自行指定,不一定是请求路径的某一段。。。

    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
        <soap:Body>
            <getRandomCode xmlns="http://webservice.linkedbear.com/">
                <name>LinkedBear</name>
            </getRandomCode>
        </soap:Body>
    </soap:Envelope>
    

    8.3.4 修改服务提供者源码

    要想使WebService支持SOAP的POST请求访问,就需要在每个参数上加上注解标识,代表可以被SOAP的请求使用。

    修改后的源码如下(注意看参数上面的注解):

    @WebService
    public class JWSProvider {
        public String getRandomCode(
                    @WebParam(name="name",targetNamespace="http://webservice.linkedbear.com/") String name
                ) {
            System.out.println("基于JWS的WebService服务:getRandomCode被调用了。。。");
            return name + Math.random();
        }
        
        public static void main(String[] args) throws Exception {
            Endpoint.publish("http://localhost/getRandomCode", new JWSProvider());
        }
    }
    

    8.3.5 使用HttpClient发送POST请求,调用WebService服务

    public class App {
        public static void main(String[] args) throws Exception {
            String url = "http://localhost/getRandomCode?wsdl";
            StringBuilder sb = new StringBuilder();
            sb.append("<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">");
            sb.append("  <soap:Body>");
            sb.append("    <getRandomCode xmlns=\"http://webservice.linkedbear.com/\">");
            sb.append("      <name>LinkedBear</name>");
            sb.append("    </getRandomCode>");
            sb.append("  </soap:Body>");
            sb.append("</soap:Envelope>");
            PostMethod postMethod = new PostMethod(url);
            byte[] bytes = sb.toString().getBytes("utf-8");
            InputStream inputStream = new ByteArrayInputStream(bytes, 0, bytes.length);
            RequestEntity requestEntity = new InputStreamRequestEntity(inputStream, bytes.length, "text/xml;charset=UTF-8");
            postMethod.setRequestEntity(requestEntity);
            
            HttpClient httpClient = new HttpClient();
            httpClient.executeMethod(postMethod);
            String soapResponseData = postMethod.getResponseBodyAsString();
            System.out.println(soapResponseData);
        }
    }
    

    请求结果(响应体真的没有换行符号,直接一行出来了。。。):

    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:getRandomCodeResponse xmlns:ns2="http://jws.webservice.linkedbear.com/"><return>LinkedBear0.0681470650599495</return></ns2:getRandomCodeResponse></soap:Body></soap:Envelope>

    8.3.6 提取响应数据

    我们完全可以使用Dom4j来提取响应体的数据,但是Dom4j只能一层一层的扒,太费劲。我推荐大家使用Jsoup进行xml转换和提取。

    导入Jsoup的jar如下:

    <dependency>
    	<groupId>org.jsoup</groupId>
    	<artifactId>jsoup</artifactId>
    	<version>1.11.3</version>
    </dependency>
    

    之后向刚才的源码追加如下内容,便可以只输出想要的返回结果。

    Document document = Jsoup.parse(soapResponseData);
    String text = document.getElementsByTag("return").text();
    System.out.println(text);
    //输出结果:
    //LinkedBear0.0681470650599495
    

    9. 【附加】制造通用的基于SOAP的WebService请求工具

     

    本来上面已经做完了,但是为了后期方便,就封装一套工具吧!这样以后就可以方便调用了。

    设计的原则:尽可能的符合“开放-封闭原则”。

    9.1 设计基本请求工具类

    最基本的工具类,只需要将刚才上面列出来的SOAP协议请求体数据提取出一个Map,封装进即可。

    初步封装如下:

    public class WebServiceUtil {
        public static void invokeWebService(Map<String, Object> map) throws Exception {
            String url = (String) map.get("url");
            StringBuilder sb = new StringBuilder();
            sb.append("<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">");
            sb.append("  <soap:Body>");
            //传入method和namespace
            sb.append("    <" + map.get("method") + " xmlns=\"" + map.get("namespace") + "\">");
            //动态构造参数和值
            Map<String, String> argsMap = (Map<String, String>) map.get("argsMap");
            for (Map.Entry<String, String> entry : argsMap.entrySet()) {
                sb.append("      <" + entry.getKey() + ">" + entry.getValue() +"</" + entry.getKey() + ">");
            }
            sb.append("    </" + map.get("method") + ">");
            sb.append("  </soap:Body>");
            sb.append("</soap:Envelope>");
            PostMethod postMethod = new PostMethod(url);
            byte[] bytes = sb.toString().getBytes("utf-8");
            InputStream inputStream = new ByteArrayInputStream(bytes, 0, bytes.length);
            RequestEntity requestEntity = new InputStreamRequestEntity(inputStream, bytes.length, "text/xml;charset=UTF-8");
            postMethod.setRequestEntity(requestEntity);
            
            HttpClient httpClient = new HttpClient();
            httpClient.executeMethod(postMethod);
            String soapResponseData = postMethod.getResponseBodyAsString();
            
            //提取响应体中的指定标签内的数据
            Document document = Jsoup.parse(soapResponseData);
            List<String> returnTagList = (List<String>) map.get("returnTagList");
            for (String returnTag : returnTagList) {
                Elements tags = document.getElementsByTag(returnTag);
                for (Element tag : tags) {
                    System.out.println(tag.text());
                }
            }
        }
        
        private WebServiceUtil() {
        }
    }
    

    9.2 二度封装:参数封装为数据结构,并加入空值校验

    上面的封装有很多不妥之处:

    1. 参数的key值属于魔法值,随时都有可能修改;
    2. 每次从map中get值时可能取到空值;
    3. 从map中取出的集合内元素也有可能为null。

    针对以上问题,需要构造一个WebServiceSoapBean的数据结构,用来封装请求中出现的数据。

    二度封装后的源码如下(为了更方便的做非空判定,加入了commons-lang):

    public class WebServiceSoapBean {
        private String url;
        private String method;
        private String namespace;
        private Map<String, String> argsMap;
        private List<String> returnTagList;
        //getter, setter
    }
    
    public class WebServiceUtil {
        public static void invokeWebService(WebServiceSoapBean bean) throws Exception {
            checkBeanIsComplete(bean);
            
            String url = bean.getUrl();
            StringBuilder sb = new StringBuilder();
            sb.append("<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">");
            sb.append("  <soap:Body>");
            //传入method和namespace
            sb.append("    <" + bean.getMethod() + " xmlns=\"" + bean.getNamespace() + "\">");
            //动态构造参数和值
            Map<String, String> argsMap = bean.getArgsMap();
            for (Map.Entry<String, String> entry : argsMap.entrySet()) {
                sb.append("      <" + entry.getKey() + ">" + entry.getValue() +"</" + entry.getKey() + ">");
            }
            sb.append("    </" + bean.getMethod() + ">");
            sb.append("  </soap:Body>");
            sb.append("</soap:Envelope>");
            PostMethod postMethod = new PostMethod(url);
            byte[] bytes = sb.toString().getBytes("utf-8");
            InputStream inputStream = new ByteArrayInputStream(bytes, 0, bytes.length);
            RequestEntity requestEntity = new InputStreamRequestEntity(inputStream, bytes.length, "text/xml;charset=UTF-8");
            postMethod.setRequestEntity(requestEntity);
            
            HttpClient httpClient = new HttpClient();
            httpClient.executeMethod(postMethod);
            String soapResponseData = postMethod.getResponseBodyAsString();
            
            //提取响应体中的指定标签内的数据
            Document document = Jsoup.parse(soapResponseData);
            List<String> returnTagList = bean.getReturnTagList();
            for (String returnTag : returnTagList) {
                Elements tags = document.getElementsByTag(returnTag);
                for (Element tag : tags) {
                    System.out.println(tag.text());
                }
            }
        }
        
        private static void checkBeanIsComplete(WebServiceSoapBean bean) {
            if (StringUtils.isBlank(bean.getUrl())) {
                throw new NullPointerException("对不起,url为空!");
            }
            if (StringUtils.isBlank(bean.getMethod())) {
                throw new NullPointerException("对不起,method为空!");
            }
            if (StringUtils.isBlank(bean.getNamespace())) {
                throw new NullPointerException("对不起,namespace为空!");
            }
            if (Objects.isNull(bean.getArgsMap())) {
                throw new NullPointerException("对不起,参数列表为null!");
            }
            if (Objects.isNull(bean.getReturnTagList())) {
                throw new NullPointerException("对不起,响应体标签列表为null!");
            }
        }
    
        private WebServiceUtil2() {
        }
    }
    

    9.3 三度封装:优化响应体数据打印

    上面的封装,对于数据的处理没有什么太大的问题了,但是打印出来的响应数据却会比较乱。

    接下来要将响应体的数据提取单独分离出一个方法,且提取返回类型应为Map或json。

    继续添加fastjson的依赖(JSONObject比Map更强大,且有序,底层LinkedHashMap实现)。

    三度封装后的源码如下(只有一个新加的方法):

    //新加的方法:
    private static List<JSONObject> parseResponseXmlToJson(String xml, List<String> returnTagList) {
        //转换之前先校验:如果压根就没有这个标签,直接抛空指针
        if (returnTagList.isEmpty()) {
            throw new NullPointerException("对不起,响应体标签集合为空!");
        }
        for (String tag : returnTagList) {
            //不能只校验标签,还要加上尖括号,防止出现响应数据中出现tag而撞车
            if (!xml.contains("<" + tag + ">")) {
                throw new NullPointerException("对不起,响应体中没有这个标签:" + tag);
            }
        }
        
        Document document = Jsoup.parse(xml);
        //前面校验过了,所以这里肯定能取出来
        Element parent = document.getElementsByTag(returnTagList.get(0)).get(0).parent();
        //取同级,每一个同级就是一个json
        String tagName = parent.tagName();
        //再退一级,找这个tagName,就可以获取所有数据结点了
        Elements elements = parent.parent().getElementsByTag(tagName);
        List<JSONObject> list = new ArrayList<>(elements.size());
        for (Element element : elements) {
            JSONObject json = new JSONObject();
            for (String returnTag : returnTagList) {
                json.put(returnTag, element.getElementsByTag(returnTag).get(0).text());
            }
        }
        
        return list;
    }
    

    9.4 四度封装:模板方法模式

    目前的设计已经大体具备了一个通用工具类应该有的功能,这样就结束了吗?

    需求:个性化定制WebService请求,包括额外的参数、额外的校验、额外的提取规则等等???

    那通用工具类就远远不够了,需要进行纵向扩展。

    在面向对象的思想中,纵向扩展的实现方案是:继承和多态。

     

    那么接下来我们来分析:通用工具类和个性化定制的请求类,到底差别在哪里?如何分离共性?如何实现个性?

    这就需要使用模板方法模式进行更深层次的通用抽取。

    既然使用模板方法模式,就不能再做静态工具类了,而是纵向扩展的类继承体系。

    最终封装的模板类如下(解释一个问题:这个类没有设计成抽象类,是因为即便没有特殊的需求下,普通WebService也可以直接通过这个类去实例化对象,调用服务接口,而不是每次都要创建一个匿名内部类):

    public class WebServiceTemplateUtil {
        public void invokeWebService(WebServiceSoapBean bean) throws Exception {
            checkBeanIsComplete(bean);
            
            String url = bean.getUrl();
            StringBuilder sb = new StringBuilder();
            sb.append("<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">");
            //模板方法切入点:有加入head部分的需求可以让子类重写此方法
            doAddSoapHead(sb, bean);
            sb.append("  <soap:Body>");
            //传入method和namespace
            sb.append("    <" + bean.getMethod() + " xmlns=\"" + bean.getNamespace() + "\">");
            //动态构造参数和值
            Map<String, String> argsMap = bean.getArgsMap();
            for (Map.Entry<String, String> entry : argsMap.entrySet()) {
                sb.append("      <" + entry.getKey() + ">" + entry.getValue() +"</" + entry.getKey() + ">");
            }
            sb.append("    </" + bean.getMethod() + ">");
            sb.append("  </soap:Body>");
            sb.append("</soap:Envelope>");
            PostMethod postMethod = new PostMethod(url);
            byte[] bytes = sb.toString().getBytes("utf-8");
            InputStream inputStream = new ByteArrayInputStream(bytes, 0, bytes.length);
            RequestEntity requestEntity = new InputStreamRequestEntity(inputStream, bytes.length, "text/xml;charset=UTF-8");
            postMethod.setRequestEntity(requestEntity);
            
            HttpClient httpClient = new HttpClient();
            httpClient.executeMethod(postMethod);
            String soapResponseData = postMethod.getResponseBodyAsString();
            
            //提取响应体中的指定标签内的数据
            List<JSONObject> jsons = parseResponseXmlToJson(soapResponseData, bean.getReturnTagList());
            for (JSONObject json : jsons) {
                System.out.println(json.toJSONString());
            }
        }
        
        //如果有加入head部分的需求可以重写此方法
        protected void doAddSoapHead(StringBuilder sb, WebServiceSoapBean bean) {
        }
        //如果有额外的数据校验的需求可以重写此方法
        protected void doExtraRequestCheck(WebServiceSoapBean bean) {
        }
        
        private List<JSONObject> parseResponseXmlToJson(String xml, List<String> returnTagList) {
            //转换之前先校验:如果压根就没有这个标签,直接抛空指针
            if (returnTagList.isEmpty()) {
                throw new NullPointerException("对不起,响应体标签集合为空!");
            }
            for (String tag : returnTagList) {
                //不能只校验标签,还要加上尖括号,防止出现响应数据中出现tag而撞车
                if (!xml.contains("<" + tag + ">")) {
                    throw new NullPointerException("对不起,响应体中没有这个标签:" + tag);
                }
            }
            
            Document document = Jsoup.parse(xml);
            //前面校验过了,所以这里肯定能取出来
            Element parent = document.getElementsByTag(returnTagList.get(0)).get(0).parent();
            //取同级,每一个同级就是一个json
            String tagName = parent.tagName();
            //再退一级,找这个tagName,就可以获取所有数据结点了
            Elements elements = parent.parent().getElementsByTag(tagName);
            List<JSONObject> list = new ArrayList<>(elements.size());
            for (Element element : elements) {
                JSONObject json = new JSONObject();
                for (String returnTag : returnTagList) {
                    json.put(returnTag, element.getElementsByTag(returnTag).get(0).text());
                }
            }
            
            return list;
        }
        
        private void checkBeanIsComplete(WebServiceSoapBean bean) {
            if (StringUtils.isBlank(bean.getUrl())) {
                throw new NullPointerException("对不起,url为空!");
            }
            if (StringUtils.isBlank(bean.getMethod())) {
                throw new NullPointerException("对不起,method为空!");
            }
            if (StringUtils.isBlank(bean.getNamespace())) {
                throw new NullPointerException("对不起,namespace为空!");
            }
            if (Objects.isNull(bean.getArgsMap())) {
                throw new NullPointerException("对不起,参数列表为null!");
            }
            if (Objects.isNull(bean.getReturnTagList())) {
                throw new NullPointerException("对不起,响应体标签列表为null!");
            }
            
            //模板方法模式切入点:有额外的数据校验的需求可以让子类重写此方法
            doExtraRequestCheck(bean);
        }
    }
    

    如果子类还需要额外的方法,可以重写模板方法达到目的。

    这样就满足了“开放-封闭原则”——“对扩展开放,对修改关闭”。

    (完)文章转自: https://dwz.pm/7v

    展开全文
  • WebService 介绍 Web service 是一个平台独立的,低耦合的 web 的应用程序用于开发分布式的互操作的应用程序。Web Service 技术, 能使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件, 就...

    WebService 介绍

    Web service 是一个平台独立的,低耦合的 web 的应用程序用于开发分布式的互操作的应用程序。Web Service 技术, 能使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件, 就可相互交换数据或集成。

    SOAP(Simple Object Access Protocol) 简单对象访问协议:

    SOAP=http+xml

    WSDL(Web Services Description Language) Web Service 的描述语言:

    一个 webservice 服务的说明书,通过该说明书可以完成 webservice 服务的调用

     

    服务提供者实现

    创建 ws-provider 项目

     

    创建 UserService 接口

    package com.bjsxt.service;
    
    public interface UserService {
        public String syaHello(String name);
    }
    

     

    创建 UserServiceImpl 实现类

    package com.bjsxt.service.impl;
    
    import com.bjsxt.service.UserService;
    
    import javax.jws.WebService;
    
    @WebService
    public class UserServiceImpl implements UserService {
        @Override
        public String syaHello(String name) {
            return "name: "+name;
        }
    }
    

     

    发布 webservice 服务

    package com.bjsxt.app;
    
    import com.bjsxt.service.UserService;
    import com.bjsxt.service.impl.UserServiceImpl;
    
    import javax.xml.ws.Endpoint;
    
    public class WsProviderApp {
        public static void main(String[] args) {
            //发布的webService服务访问地址
            String address="http://localhost:9999/ws";
            //创建UserService对象
            UserService userService=new UserServiceImpl();
            Endpoint.publish(address,userService);
            System.out.println("============发布WebService服务=============");
        }
    }
    

     

    获取 wdsl 文档

     

     

    服务消费者实现

    创建 ws-consumer 项目

     

    wsimport 生成消费者代码 

     

    消费远程服务

    package com.bjsxt.app;
    import com.bjsxt.client.UserServiceImpl;
    import com.bjsxt.client.UserServiceImplService;
    public class WsConsumerApp {
    public static void main(String[] args) {
            /***
            * 完成 webservice 服务的消费
            */
            //创建服务类对象
            UserServiceImplService service=new UserServiceImplService();
            //获得远程服务的代理对象
            UserServiceImpl userService=service.getUserServiceImplPort();
            System.out.println(userService.getClass().getName());
            //进行远程服务调用
            String result = userService.sayHello("张三");
            System.out.println("result="+result);
        }
    }

     

    WEBSERVICE 相关 API 总结

    展开全文
  • RPC能省略部分接口代码的开发,可以跨机器之间访问对象(Java RMI),可以有更方便的加密更高效的数据传输性能,而不需要了解底层网络技术的协议。RPC不仅可以走HTTP/HTTPS,也可以自定义TCP协议,从而省略HTTP...

    RPC(Remote Procedure Call,远程过程调用)是一个很大的概念。它是一种通过网络从远程计算机程序上跨语言跨平台的请求服务。RPC能省略部分接口代码的开发,可以跨机器之间访问对象(Java RMI),可以有更方便的加密和更高效的数据传输性能,而不需要了解底层网络技术的协议。RPC不仅可以走HTTP/HTTPS,也可以自定义TCP协议,从而省略HTTP繁杂的规则和冗余信息。

    WebService是一种跨编程语言和跨操作系统平台的远程调用技术,实际上就是RPC调用的一种实现。

    常用的RPC框架有Dubbo、RML、Grpc、Hessian等。

    RPC的主要作用是解决分布式系统中服务之间的调用问题,能够在远程调用时,像调用本地方法一样方便,让调用者感知不到远程调用的逻辑。RPC主要是用在大型企业里面,因为大型企业里面系统繁多,业务线复杂,而且效率优势非常重要。而由于小型企业一般业务简单,不需要进行分布式架构,小型企业主要还是使用WebService中的RESTful WebService,部分特殊业务会使用Soap WebService。

     

    "听到你说你要离开了,我的心突然有点疼。"

    转载于:https://www.cnblogs.com/yanggb/p/10872762.html

    展开全文
  • web service顾名思义就是一个运行在web上的服务。这个服务通过网络为我们的程序提供服务方法。类似一个远程的服务提供者。...Web Service 是一个能够实现远程数据交互的一个技术协议,通过HTML进行通讯。 他实现了...
  • WebServices服务接口调用---基于rpc方式应用。sun-jaxws.xml方式
  • RPC架构webService简单实现
  • http对于复杂大量的远程调用不太方便,不利于开发,所以就衍生出了webservice、restful、rpc。 websocket主要是解决长连接问题,不像http一问一答,而是全双工通信,可以相互通信。它不是为了解决频繁的远程调用,是...
  • 关于架构方面有很多名词,有点晕头转向,今天就针对相关技术RPC/Web Service/REST/SOA/SOAP理解做下记录。 一、简介 先对上面的名词做一个概要介绍: RPC,远程过程调用 (面向方法),你可以这么理解,就是在另外一...
  • 使用RPC方式调用WebService

    千次阅读 2016-04-17 10:48:53
    使用RPC方式调用WebService的步骤如下: 1)创建RPCServiceClient对象 2)创建EndpointReference对象,并指定要访问WebService的URL(不包含wsdl) 3)创建QName对象,并指定要调用的WebServ...
  • JAVA rpc调用webservice服务

    千次阅读 2018-11-21 17:02:56
    public class RPCClient {  private static final String arg0 = "&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n"  +" \r\n"  ...
  • WebService学习总结 SOAP协议 WebService两种实现方式SOAP与Rest比较 RPC概念及原理 HttpClient RPC与Rest区别 RPC与RMI区别: https://cloud.tencent.com/developer/article/1353191 Dubbo各协议的比较:...
  • 效率高于http,但只能在调用时发现错误,常用的RPC框架有:dubbo,thrift,grpc等, 适合大型企业服务http基于http协议,属于应用层协议,编译时期可以检查数据格式,迭代快,适合中小型企业webservice也是基于http...
  • 1.传统的WebService的只要架构就是 1.注册方 服务器的提供者 2.提供方 WebService业务实现方,提供接口 3.消费方 调用方 实现方式: 1.业务接口 ProductService @WebService public interface ...
  • dubbo-rpc-webservice-2.8.4

    2017-07-10 15:37:55
    dubbo-rpc-webservice-2.8.4 dubbo-rpc-webservice-2.8.4
  • rpc和document的区别也是在soap的下的区别(it is SOAP thatthe RPC/document distinction refers to)。通常http(s)是传输soap消息的协议。 The <wsdl:binding> element of the WSDL contains a pair of ...
  • WebService :: Aria2 :: RPC-通过RPC接口控制aria2实例 概要 use WebService::Aria2::RPC::JSON; my $aria2 = WebService::Aria2::RPC::JSON->new(); my $gid = $aria2->add_uri( 'http://example.com/file.tgz' ); ...
  • WebserviceRPC

    2013-08-28 22:36:48
    RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过...在OSI网络通信模型中,RPC跨越了传输层应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。   基本简介  
  • 1.项目环境:WebService服务端Delphi开发,客户端Eclipse JDK1.8, 需要Jar包activation.jar,mail.jar,soap.jar 2.WebService接口服务信息,输入地址http://ServerIP:8081/,访问内容如下: HGXSWebServer - ...
  • RPC方式调用WebService

    千次阅读 2013-11-08 10:07:55
    //使用RPC调用WebService RPCServiceClient serviceClient = new RPCServiceClient(); Options options = serviceClient.getOptions(); //指定调用WebService的URL,这个地方是wsdl的地址,但是后面的?wsdl不要加上...
  • java 1.8  javax.xml.rpc javax.xml.rpc-api 1.1.1 org.apache.axis2 axis2-kernel 1.6.4 org.apache.axis2 axis2-adb 1.6.4
  • SOA、RMI、RPC、Rest、RestFul、Soap、WebService详解 目录 一、SOA是什么? SOA的应用场景: SOA主要的使用场景: ​ 数据总线是什么? SOA最显著的优势: SOA与微服务架构的区别: 二、WebService是什么...
  • webserviceRPCClient 远程调用服务

    千次阅读 2017-12-25 15:01:11
    package com.primeton.eos; import javax.xml.namespace.QName; import org.apache.axis2.addressing.EndpointReference;...import org.apache.axis2.client.Options;...import org.apache.axis2.rpc.client.RPC
  • WebService

    2019-12-18 22:56:16
    WebService: web服务,是只部署在网络上的可访问的应用程序,对外暴露业务接口 1、webService的组成 webService的核心思想是方法的远程调用 1、注册方 2、服务提供者 3、消费者(客户端) 2、webService的两种方式 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,707
精华内容 10,282
关键字:

webservice和rpc