聊一聊网络编程中RMI远程调用

4 篇文章 0 订阅

原文链接聊一聊网络编程中RMI远程调用

1.网络编程

网络编程是开发中经常需要用到的一个点,也是面试中必考题,本节对RMI远程调用做一下简单的演示。

❝以下知识点均来源于廖雪峰官方网站https://www.liaoxuefeng.com/wiki/1252599548343744/1323711850348577

Java的RMI远程调用是指一个JVM中的代码可以通过网络实现远程调用另一个JVM的某个方法。RMI是**「Remote Method Invocation」**的缩写。提供服务的一方我们称之为服务器,而实现远程调用的一方我们称之为客户端。

2.实例解析

我们先来实现一个最简单的RMI:服务器会提供一个**「WorldClock」**服务,允许客户端获取指定时区的时间,即允许客户端调用下面的方法:

LocalDateTime getLocalDateTime(String zoneId);

要实现RMI,服务器和客户端必须共享同一个接口。我们定义一个**「WorldClock」**接口,代码如下:

public interface WorldClock extends Remote {  
    LocalDateTime getLocalDateTime(String zoneId) throws RemoteException;  
}  

Java的RMI规定此接口必须派生自**「java.rmi.Remote」,并在每个方法声明抛出「RemoteException」。下一步是编写服务器的实现类,因为客户端请求的调用方法「getLocalDateTime()」最终会通过这个实现类返回结果。实现类「WorldClockService」**代码如下:

public class WorldClockService implements WorldClock {    @Override    public LocalDateTime getLocalDateTime(String zoneId) throws RemoteException {        return LocalDateTime.now(ZoneId.of(zoneId)).withNano(0);    }}

现在,服务器端的服务相关代码就编写完毕。我们需要通过Java RMI提供的一系列底层支持接口,把上面编写的服务以RMI的形式暴露在网络上,客户端才能调用:

/**  
 * @Auther Mario  
 * @Date 2021-01-06 10:20  
 * @Version 1.0  
 * 现在,服务器端的服务相关代码就编写完毕。  
 * 我们需要通过Java RMI提供的一系列底层支持接口,  
 * 把上面编写的服务以RMI的形式暴露在网络上,客户端才能调用:  
 */  
public class Server {  
  
    public static void main(String[] args) throws Exception {  
        System.out.println("create WorldClock remote service.....");  
        //实例化一个WorldClock  
        WorldClock worldClock = new WorldClockService();  
        //将此接口转换为远程服务接口  
        WorldClock skeleton = (WorldClock) UnicastRemoteObject.exportObject(worldClock,0);  
        //将RMI服务注册到1099端口  
        Registry registry = LocateRegistry.createRegistry(1099);  
        //注册此服务,服务名为"worldClock";  
        registry.rebind("WorldClock",skeleton);  
    }  
}  

上述代码主要目的是通过RMI提供的相关类,将我们自己的WorldClock实例注册到RMI服务上。RMI的默认端口是**「1099」,最后一步注册服务时通过「rebind()」** 指定服务名称为 「“WorldClock”」。下一步我们就可以编写客户端代码。RMI要求服务器和客户端共享同一个接口,因此我们要把**「WorldClock.java」**这个接口文件复制到客户端,然后在客户端实现RMI调用:

public class Client {  
    public static void main(String[] args) throws Exception {  
        //链接到服务器localhost,端口1099  
        Registry registry = LocateRegistry.getRegistry("localhost",1099);  
        //查找名称为"WorldClock"的服务并强制转型为WorldClock接口  
        WorldClock worldClock = (WorldClock) registry.lookup("WorldClock");  
        //正常调用接口方法  
        LocalDateTime now = worldClock.getLocalDateTime("Asia/Shanghai");  
        //打印调用结果  
        System.out.println(now);  
  
    }  
}  

结果解析

image

image

先运行服务器,再运行客户端。从运行结果可知,因为客户端只有接口,并没有实现类,因此,客户端获得的接口方法返回值实际上是通过网络从服务器端获取的。整个过程实际上非常简单,对客户端来说,客户端持有的 「WorldClock」 接口实际上对应了一个“实现类”,它是由 「Registry」 内部动态生成的,并负责把方法调用通过网络传递到服务器端。而服务器端接收网络调用的服务并不是我们自己编写的 「WorldClockService」, 而是 「Registry」 自动生成的代码。我们把客户端的“实现类”称为 「stub」,而服务器端的网络服务类称为 「skeleton」,它会真正调用服务器端的 「WorldClockService」,获取结果,然后把结果通过网络传递给客户端。整个过程由RMI底层负责实现序列化和反序列化:

image

Java的RMI严重依赖序列化和反序列化,而这种情况下可能会造成严重的安全漏洞,因为Java的序列化和反序列化不但涉及到数据,还涉及到二进制的字节码,即使使用白名单机制也很难保证100%排除恶意构造的字节码。因此,使用RMI时,双方必须是内网互相信任的机器,不要把 「1099」 端口暴露在公网上作为对外服务。此外,Java的RMI调用机制决定了双方必须是Java程序,其他语言很难调用Java的RMI。如果要使用不同语言进行RPC调用,可以选择更通用的协议, 例如**「gRPC」**。

总结

在本节前一篇HTTP编程中引入java.net时出现错误,导致在编译本节代码模块出现诸多原因,耽误了一些时间,总的来说,革命尚未成功,我辈仍需努力呀!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值