当前位置: 首页 > news >正文

接口开发 — — RPC远程过程调用实现

接口开发 — — RPC远程过程调用实现

我们在日常开发过程中,经常会遇到与第三方交互的情况,这个时候就会涉及到RPC(Remote Procedure Call)远程过程调用。

比如下表:
在这里插入图片描述
其实,这就涉及到RPC的实现了,那么什么是RPC呢?RPC又是怎么实现的呢?

1 介绍

RPC(Remote Procedure Call):远程过程调用,是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。请求程序就是一个客户端,而服务提供程序就是一个服务器。
在这里插入图片描述

2 应用场景

支付宝、微信、银联等第三方支付接入

3 实现方式

3.1 RMI

RMI:Java提供的基于Java平台的RPC远程调用技术,服务消费者和服务提供者是Java平台。

在这里插入图片描述

3.1.1 RMI实现步骤

新建rmi-provider项目

结构图:
在这里插入图片描述

3.1.1.1 创建UserService接口

用于提供服务;实现接口时,需要抛出异常【强制】

/**
 * 创建需要发布的服务对应的业务接口
 * Remote 接口用于标识其方法可以从非本地虚拟机上调用的接口
 */
public interface UserService extends Remote {

    public String helloRMI(String name) throws RemoteException;
}
3.1.1.2 创建UserServiceImpl实现类

一定要继承UnicastRemoteObject类,不然会发布失败


/**
 * 创建发布的服务对应的实现类
 */
public class UserServiceImpl extends UnicastRemoteObject implements UserService {
    
    public UserServiceImpl() throws RemoteException {
        super();
    }
    
    @Override
    public String helloRMI(String name) {
        return "hello:" + name;
    }
}
3.1.1.3 发布远程服务【ProviderApp】
/**
 * 发布远程服务
 */
public class ProviderApp {
    public static void main(String[] args) {
        try {
            //发布服务的端口
            LocateRegistry.createRegistry(8888);
            //发布远程服务的URL
            String name = "rmi://localhost:8888/rmi";
            //创建一个提供具体服务的远程对象
            UserService userService = new UserServiceImpl();
            //给提供远程服务的对象绑定一个URL
            Naming.bind(name, userService);
            System.out.println("发布RMI远程服务成功");
        } catch (Exception e) {
            System.out.println("发布失败.....");
            e.printStackTrace();
        }
    }
}

结果:
在这里插入图片描述

3.1.1.4 服务消费者实现【拷贝UserService接口】

创建rmi-consumer项目

项目结构:
在这里插入图片描述

//从服务提供端拷贝来的接口【与远程服务提供方保持一致】
public interface UserService {
    
    public String helloRMI(String name);
}
3.1.1.5 消费远程服务【ConsumerApp】
import java.rmi.Naming;

//消费远程服务
public class ConsumerApp {

    public static void main(String[] args) {
        try {
            //访问远程服务的URL
            String name = "rmi://localhost:8888/rmi";
            //通过发布的远程服务的URL,获得远程服务的代理对象
            UserService userService = (UserService) Naming.lookup(name);
            System.out.println("获得远程服务的代理对象" + userService.getClass().getName());
            //通过远程服务的代理对象调用远程服务的方法
            String result = userService.helloRMI("jack");
            System.out.println("result:" + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果:
在这里插入图片描述

3.1.2 RMI实现过程的常见错误

java的 rmi远程调用给分布式编程带来极大的方便,在使用rmi过程中若遇到以下两个问题,可以尝试如下的解决方法

  • 错误一:
 java.rmi.server.ExportException : remote object implements illegal remote 
 interface; nested exception is : java.lang.IllegalArgumentException : 
 illegal remote method encountered : ,
 
"解决办法":如果这样提示说明接口函数没有涉及异常抛出,在所提示的函数后面加
上throws RemoteException就可以解决
  • 错误二:
exception: java.rmi.UnmarshalException: error unmarshalling return; nested 
exception is: java.io.WriteAbortedException: writing aborted; 
java.io.NotSerializableException :

"解决办法":若这样提示,表明某个类是不能被序列化的,需要在该类上加上implements 
Serializable,就可以解决

需要注意的是,Java的RMI远程调用的两个站点可能需要编写相同的代码,保证不提示什么错误,就可以正常运行

3.1.3 RMI总结

名称含义
Remote接口标识某个方法可以被远程调用
UnicastRemoteObject类实现Remote远程对象的导出
Naming给提供远程服务的对象绑定URL,通过远程的URL,获得提供远程服务的代理对象
LocateRegistry类指定发布的远程服务的方法接口

实现步骤:
①创建服务接口
②实现接口内容【具体服务内容】
③发布服务
④消费方拷贝服务接口
⑤消费方消费远程服务

3.2 WebService【http+xml】

3.2.1 定义

通过Http协议,请求发送XML和响应XML的RPC远程调用技术,最大的特征就是使用XML进行数据交互,可以实现跨平台调用。

WebService也叫XML Web Service,WebSerice是一种可以接收从Internet或者Intranet上的其他系统中传递过来的请求,轻量级的独立的通讯服务。是通过SOAP在Web上提供对的软件服务,使用WSDL文件进行说明,并通过UUDI进行注册。

总的来说,WebService就是一种跨编程语言和操作系统平台的远程调用技术

从多个维度理解WebService:

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

在这里插入图片描述

3.2.2 核心要素【SOAP、WSDL、UDDI】

SOAP、WSDL、UDDI(UniversalDescriptionDiscovery andIntegration)三者构成了WebService的三要素。

  • SOAP(Simple Object Access Protocol):

    WebService通过HTTP协议发送请求和接收结果时,发送的请求内容和结果都是采用XML格式封装,并增加了一些特定的HTTP消息头,以说明HTTP消息的内容格式,这些特定的HTTP消息头和XML格式就是SOAP协议。SOAP提供了标准的RPC方法来调用WebService。

    SOAP协议组成:
    SOAP协议 = HTTP协议+XML数据格式

    SOAP协议定义了SOAP消息的格式,SOAP协议是基于HTTP协议的,SOAP也是基于XML和XSD的,XML是SOAP的数据编码方式。打个比喻就是:HTTP是普通公路,XML是中间的绿色隔离带和两边的防护栏,SOAP就是普通公路经过隔离带和防护栏改造过的高速公路。

  • WSDL
    就像是我们区商店买东西,首先要知道商店里有什么东西可以买,然后再来购买。商家的做法就是张贴海报。WebService也一样,WebSerive客户端要调用一个WebSerivce服务,首先要知道这个服务的地址在哪,以及这个服务里有什么方法可以调用。所以,WebService服务器端首先要通过一个WSDL文件来说明自己家里有啥服务可以对外调用,服务是什么(服务中有哪些方法,方法接受的参数是什么,返回值是什么),服务的网络地址用哪个URL地址表示,服务通过什么方式来调用。

    WSDL(Web Services Description Language)就是这样一个基于XML的语言,用于描述WebService及其函数、参数、返回值。它是WebService客户端和服务端都能理解的标准格式。因为是基于XML的,所以WSDL既是机器可读的,又是人可阅读的,这是一个很大的好处。【一些开发工具可以根据我们的WebService生成WSDL文档,又可以导入WSDL文档,生成调用相应的WebService的代理类代码】。

  • UDDI
    UDDI(Universal Description, Discover, and Integeration)是一个主要针对Web服务供应商和使用者的新项目。在用户能够调用Web服务之前,必须确定这个服务内包含哪些方法,找到调用的接口的定义,还要在服务端来编制软件,UDDI是一种根据描述文档来以电脑系统查找相应服务的机制。
    UDDI利用SOAP消息机制(标准的XML/HTTP)来发布,编辑,浏览以及查找注册信息。它采用XML格式来封装各种不同类型的数据,并且发送到注册中心或由注册中心来返回需要的数据。

3.2.3 调用原理

在这里插入图片描述
实现一个完整的Web服务工作流程:

  1. 【注册】Web服务提供者设计实现Web服务,并将调试正确后的Web服务通过Web服务中介者发布,并在UDDI注册中心注册
  2. 【请求服务】Web服务请求者向Web服务中介者请求特定的服务,中介者根据请求查询UDDI注册中心,为请求者寻找满足请求的服务
  3. 【返回服务描述信息】Web服务中介者向Web服务请求者返回满足条件的Web服务描述信息,该信息用WSDL写成,各种支持Web服务的机器都能阅读。
  4. 【发送SOAP消息】利用从Web服务中介者返回的描述信息生成相应的SOAP消息,发送给Web服务提供者,以实现Web服务调用
  5. 【返回执行结果】Web服务提供者按SOAP消息执行相应的Web服务,并将服务结果返回给Web服务请求者

3.2.4 使用场景

"适用范围":应用程序跨平台、跨网络。适用于应用程序集成B2B集成、代码和数据重用以及通过
Web进行客户端和服务器通信的场合。
"不适用场景"WebService会降低应用程序的性能,因此一台机器或者局域网里面运行的同构应用
程序就不应该用Web Service进行通信

详细说明:https://blog.csdn.net/weidawei0609/article/details/7915071

3.2.5 实现步骤

创建服务提供方ws_server项目

服务端结构:
在这里插入图片描述

3.2.5.1 创建WeatherService接口【创建服务端】
/**
 * 天气服务接口
 */
@WebService
public interface WeatherService {

    @WebMethod
    String getWeatherByCityName(String name);
}
3.2.5.2 创建WeatherService接口实现类
//创建服务实现类
@WebService
public class WeatherServiceImpl implements WeatherService {


    @Override
    public String getWeatherByCityName(String name) {
        return name + "今天艳阳高照!!!";
    }
}
3.2.5.3 发布天气服务
//发布天气
public class Main {
    public static void main(String[] args) {
        //发布天气服务【这里只是简单的进行测试】
        Endpoint.publish("http://localhost:8085/test_server/weather", 
        	new WeatherServiceImpl());
        System.out.println("发布天气服务成功...");
    }
}

在这里插入图片描述

至此,服务端程序创建发布成功,运行后可在浏览器中访问http://localhost:8085/test_server/weather?wsdl
在这里插入图片描述
可以看到,我们的服务已经发布成功了。
在这里插入图片描述

3.2.5.4 创建客户端,在cmd窗口运行命令

创建客户端项目

生成客户端代码有很多种方式,这里我们演示JDL自带的wsimport工具
  • 运行命令前:
    在这里插入图片描述
  • 运行命令
选中src,鼠标右键,选择Open in terminal打开cmd,当然,我们也可以win+R输入cmd,
打开dos命令,进入到ws_client项目的src目录。

输入wsimport -keep http://localhost:8085/test_server/weather?wsdl,点击回车,出现下图情况即为生成成功。
(还可以将wsimport -keep http://localhost:8085/test_server/weather?wsdl生成的文档以wsdl为后缀保存到项目中,输入wsimport -keep wsdl文件的路径执行)
在这里插入图片描述

  • 运行结果
    在这里插入图片描述
3.2.5.5 客户端调用服务端
/**
 * WebService客户端测试
 */
public class ClientTest {

    public static void main(String[] args) {
        //创建服务类对象
        WeatherServiceImplService service = new WeatherServiceImplService();
        //创建远程服务的代理对象
        WeatherServiceImpl weatherService = service.getWeatherServiceImplPort();
        System.out.println(weatherService.getClass().getName());
        //进行远程服务调用
        String weather = weatherService.getWeatherByCityName("杭州");
        System.out.println(weather);
    }
}

运行结果:
在这里插入图片描述

3.2.6 总结

名称作用
@WebService指定发布远程的服务(默认类中的所有方法)
@WebMethod指定类中特定的方法发布远程服务
EndPoint发布具体的远程服务,给提供远程服务的对象绑定一个URL

3.3 HttpClient【http+json】

HttpClient:Http客户端工具,Java程序通过HttpClient发送Http协议的请求,直接获得远程资源。

在这里插入图片描述

3.3.1 定义

就目前来说,HTTP协议是在Internet上使用得最多、最重要的协议的了,越来越多的Java应用程序需要直接通过HTTP协议来访问网络资源。

HttpClient是Apache Jakarta Common下的子项目,提供了高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包。实现了所有HTTP的方法(GET、POST、PUT、HEAD等八种),支持RestFul。
在这里插入图片描述

3.3.2 实现步骤

3.3.2.1 创建服务端【Controller层】
@Controller
public class DemoController {

    @RequestMapping("/demo")
    @ResponseBody
    public String demo(String param){
        return "demo" + param;
    }

}

服务端启动类:

@SpringBootApplication
public class HttpClientServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(HttpClientServerApplication.class, args);
    }

}
3.3.2.2 创建客户端【导入httpClient依赖】

导入依赖:

<!--导入依赖:httpClient-->
 <dependency>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpclient</artifactId>
     <version>4.5.10</version>
 </dependency>
3.3.2.3 GET方式请求
public class GetDemo {
    public static void main(String[] args) {
        try{
            //创建http工具(理解成:浏览器) 发起请求,解析响应
            CloseableHttpClient httpClient = HttpClients.createDefault();
            //请求路径
            URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/demo");
            uriBuilder.addParameter("param", "get123");
            //创建HttpGet的请求对象
            HttpGet get = new HttpGet(uriBuilder.build());
            //创建响应对象
            CloseableHttpResponse response = httpClient.execute(get);
            //由于响应体是字符串,因此把HttpEntity类型转换为字符串类型,并设置字符编码
            String result = EntityUtils.toString(response.getEntity(), "utf-8");
            //输出结果
            System.out.println(result);
            //释放资源[可以提到finally中释放]
            response.close();
            httpClient.close();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

3.3.2.4 POST方式请求
public class PostDemo {
    public static void main(String[] args) {
        try{
            //创建Http工具(相当于:浏览器) 发起请求,解析响应
            CloseableHttpClient httpClient = HttpClients.createDefault();
            //创建HttpPOST对象
            HttpPost post = new HttpPost("http://localhost:8080/demo");
            //所有请求参数
            ArrayList<NameValuePair> params = new ArrayList<>();
            params.add(new BasicNameValuePair("param", "123"));
            //创建HttpEntity接口的文本实现类对象,放入参数并设置编码
            HttpEntity httpEntity = new UrlEncodedFormEntity(params, "utf-8");
            //放入到HttpPost对象中
            post.setEntity(httpEntity);
            //创建响应对象
            CloseableHttpResponse response = httpClient.execute(post);
            //由于响应体是字符串,因此把HttpEntity类型转换为字符串类型
            String result = EntityUtils.toString(response.getEntity());
            //输出结果
            System.out.println(result);
            //释放资源[一般是在finally里释放]
            response.close();
            httpClient.close();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

参考文章:
https://blog.csdn.net/qq_34845394/article/details/86478208
https://www.jb51.net/article/225494.htm

相关文章:

  • JAMA子刊:孕妈妈每天喝半杯咖啡,可能让胎儿发育迟缓
  • Acrylamide-PEG-acid,ACA-PEG-COOH,丙稀酰胺-聚乙二醇-羧基可用于修饰多肽
  • 搭建个人的GPS定位系统
  • TorchProtein教程--蛋白质数据结构(2)
  • 动力学(dynamics)与动理学(kinetics)概念辨析
  • Vue | Object.defineProperty()、数据代理、计算属性、监视属性
  • 常见的排序总结
  • kvm存储池
  • T1055 判断闰年 (信息学一本通C++)
  • 【C++】-- STL之用哈希桶模拟实现unordered_set和unordered_map
  • Games104现代游戏引擎入门-lecture17游戏引擎的Gameplay玩法系统基础_Advanced AI
  • [JS]数据类型
  • Transformer 无卷积骨干网络
  • 服务器安全狗是什么意思?有什么功能?
  • MySQL主从复制与读写分离
  • 30秒的PHP代码片段(1)数组 - Array
  • Android单元测试 - 几个重要问题
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • express.js的介绍及使用
  • JavaScript 基础知识 - 入门篇(一)
  • JavaScript-Array类型
  • php面试题 汇集2
  • TCP拥塞控制
  • vue.js框架原理浅析
  • 搭建gitbook 和 访问权限认证
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 规范化安全开发 KOA 手脚架
  • 将 Measurements 和 Units 应用到物理学
  • 那些年我们用过的显示性能指标
  • 前端路由实现-history
  • 嵌入式文件系统
  • 三栏布局总结
  • 设计模式走一遍---观察者模式
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 一文看透浏览器架构
  • 怎么把视频里的音乐提取出来
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • 智能网联汽车信息安全
  • linux 淘宝开源监控工具tsar
  • 智能情侣枕Pillow Talk,倾听彼此的心跳
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (ZT)出版业改革:该死的死,该生的生
  • (七)c52学习之旅-中断
  • (十五)使用Nexus创建Maven私服
  • (十一)图像的罗伯特梯度锐化
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • .Net IE10 _doPostBack 未定义
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .pyc文件还原.py文件_Python什么情况下会生成pyc文件?
  • /dev下添加设备节点的方法步骤(通过device_create)
  • /etc/X11/xorg.conf 文件被误改后进不了图形化界面
  • @Autowired多个相同类型bean装配问题
  • [Android] Amazon 的 android 音视频开发文档