Java RMI(Remote Method Invocation,远程方法调用),Java RMI是Java编程语言中用于实现远程过程调用的应用程序编程接口(API)。它允许一个Java虚拟机(JVM)上的对象调用另一个JVM上对象的方法,就像调用本地方法一样。这种机制使得开发人员能够在网络环境中分布处理任务,提高应用程序的可扩展性和可靠性。
RMI 的基本思想是远程方法调用,即客户端调用某个方法,其本质是将这个方法的调用请求发送给服务器,由服务器代为执行,并将执行结果回送客户端。对于客户端而言,调用 RMI 方法就像调用本地方法一样,不需要任何配置;对于服务器而言,RMI相当于要处理一个来自客户端的“请求”,这个请求针对某个方法。
提供服务注册与服务获取。Server 端向 Registry 注册服务(如地址、端口等信息),Client 端从 Registry 获取远程对象的一些信息(如地址、端口等),然后进行远程调用。
提供具体服务,并注册到 Registry 中。Server 的功能:
(1)建立RMI服务器;
(2)侦听客户端连接请求;
(3)连接RMI客户端;
(4)接受客户端发送过来的要执行的方法名称、实参等信息;
(5)找到需要代理执行的方法,并使用反射机制执行该方法,将方法执行的结果回传给客户端,断开与客户端的连接。
用于调用远程方法,并接收返回结果。Client 的功能:
(1)连接RMI服务器;
(2)作为远程方法的消费者,从Registry获取远程方法的相关信息并且调用;
(3)等待服务器返回这个方法在服务器端执行的结果。
(1)启动 RMI Registry 服务,可以指定服务监听的端口,也可以使用默认的端口(1099)。
(2)Server 端在本地先实例化一个提供服务的实现类,然后通过 RMI 提供的 Naming/Context/Registry 等类的 bind 或 rebind 方法将实例化好的实现类注册到 RMI Registry 上,并对外暴露一个名称。
(3)Client 端通过本地的接口和一个已知的名称(即 RMI Registry 暴露出的名称),使用 RMI 提供的 Naming/Context/Registry 等类的lookup 方法从 RMI Service 那拿到实现类。
(1)定义远程接口:远程接口必须继承 java.rmi.Remote 接口标记,并且所有方法必须抛出 java.rmi.RemoteException 异常。
(2)实现远程接口:远程接口的具体实现类必须扩展 java.rmi.server.UnicastRemoteObject 类,并覆盖 Remote 接口中定义的所有方法。
(3)创建桩(Stub)和框架(Skeleton):桩是客户端的代理对象,它扩展了远程接口并实现了与远程对象通信的必要协议;框架是服务端的代理对象,它将远程接口的具体实现绑定到网络层。这些通常由RMI工具自动生成。
(4)启动 RMI 服务器并注册远程服务。
(5)编写客户端代码以查找和调用远程服务。
(1)定一个实体,实现 Serializable 接口,标记可以进行序列化。代码如下:
package com.hxstrive.rmi;
import java.io.Serializable;
/**
* 用户实体
* @author hxstrive.com
*/
public class User implements Serializable {
private static final long serialVersionUID = 6490921832856589236L;
private String name;
private Integer age;
private String skill;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", skill='" + skill + '\'' +
'}';
}
}(2)定义一个 RMI 接口,继承 Remote 接口。Remote 接口是 Java RMI 框架中的一个标记接口,用于标识一个接口定义的方法可以从非本地的JVM上调用。任何希望被远程调用的接口都必须扩展 java.rmi.Remote 接口,并且其方法必须声明抛出 java.rmi.RemoteException。例如:
package com.hxstrive.rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* RMI 接口
* @author hxstrive.com
*/
public interface UserService extends Remote {
/**
* 查找用户
*
* @param userId 用户ID
* @return
* @throws RemoteException
*/
User findUser(String userId) throws RemoteException;
}(3)实现 RMI 接口,必须继承 java.rmi.server.UnicastRemoteObject 类。java.rmi.server.UnicastRemoteObject 是 Java RMI(Remote Method Invocation,远程方法调用)框架中的一个类,它提供了将远程对象导出到 RMI 运行时以便客户端可以调用的功能。当你有一个实现了远程接口的类,并且你希望这个类的对象能够被远程访问时,你可以通过让这个类继承 UnicastRemoteObject 来实现。例如:
package com.hxstrive.rmi;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
/**
* 服务具体实现
* @author hxstrive.com
*/
public class UserServiceImpl extends UnicastRemoteObject implements UserService {
protected UserServiceImpl() throws RemoteException {
}
@Override
public User findUser(String userId) throws RemoteException {
// 模拟根据用户 ID 查询用户信息
if ("00001".equals(userId)) {
User user = new User();
user.setName("金庸");
user.setAge(100);
user.setSkill("写作");
return user;
}
throw new RemoteException("查无此人");
}
}(4)创建 RMI 服务端,将 RMI 接口和实现类绑定到 RMI Registry,并启动服务。代码如下:
package com.hxstrive.rmi;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
/**
* 服务端
* @author hxstrive.com
*/
public class HelloServer {
public static void main(String[] args) {
try {
UserService userService = new UserServiceImpl();
// 创建并导出接受指定 port 请求的本地主机上的 Registry 实例。
LocateRegistry.createRegistry(1900);
// 注册远程对象
Naming.bind("//localhost:1900/UserService", userService);
System.out.println("HelloServer 启动成功");
} catch (Exception e) {
e.printStackTrace();
}
}
}运行代码,启动服务端,输出如下:
HelloServer 启动成功
(5)创建客户端,通过 RMI Registry 查找远程对象,并调用方法。代码如下:
package com.hxstrive.rmi;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
/**
* 客户端
* @author hxstrive.com
*/
public class HelloClient {
public static void main(String[] args) {
try {
// 查找远程对象
UserService userService = (UserService) Naming.lookup("//localhost:1900/UserService");
// 调用远程方法
User user = userService.findUser("00001");
System.out.println(user);
} catch (MalformedURLException e) {
System.out.println("url 格式异常");
} catch (RemoteException e) {
System.out.println("创建对象异常");
e.printStackTrace();
} catch (NotBoundException e) {
System.out.println("对象未绑定");
}
}
}运行代码,启动客户端,输出如下:
User{name='金庸', age=100, skill='写作'}