rmi(Remote Method Invocation ) 學習

本文详细介绍了Java RMI(远程方法调用)的工作原理和技术细节,包括其系统结构、核心类及接口、实现步骤等。通过一个简单的示例程序,展示了如何创建远程接口、实现远程对象、编写客户端和服务端程序。
一:工作原理

RMI系统结构,在客户端和服务器端都有几层结构。
--------- ---------
| 客户对象| | 远程对象|
--------- ---------
| | | |
------------------------------ ---------------------------------
|  占位程序 Stub |  |  骨干网 Skeleton |
------------------------------- ------- ------------------------
| | | |
-----------------------------------------------------------------
|  远 程 引 用 层 Remote Reference Layer  |
------------------------------------ ----------------------------
| |
------------------------------------
|  传 输 层 Transport Layer  |
------------------------------------

方法调用从客户对象经占位程序(Stub)、远程引用层(Remote Reference
Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传
输层,向上穿过远程调用层和骨干网(Skeleton),到达服务器对象。
占位程序扮演着远程服务器对象的代理的角色,使该对象可被客户激活。
远程引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个
服务器还是多个。传输层管理实际的连接,并且追追踪可以接受方法调用的
远程对象。服务器端的骨干网完成对服务器对象实际的方法调用,并获取返
回值。返回值向下经远程引用层、服务器端的传输层传递回客户端,再向上
经传输层和远程调用层返回。最后,占位程序获得返回值。
(以上为引用他人原话)


二:rmi相关类
rmi由5个包和3个应用工具组成:

java.rmi 组织客户端的rmi类,接口和异常
java.rmi.sever 组织服务器端的rmi类,接口和异常
java.rmi.registry 组织用于管理rmi命名服务的类
java.rmi.dgc 组织用于管理分布式垃圾收集的类
java.rmi.activation 组织用于实现按需缉获的rmi服务的类

rmic 编译器,生成stub和sketon
rmiregistry 一个为rmi提供命名服务的服务器,这项服务把名字和对象关联在一起
rmid 一个支持rmi激活框架的服务器

但是大多数情况下,我们只需要使用每个包的一部分方法和接口就可以成功的实现一个使用rmi分布式解决方案.


三:步骤
要完成以上步骤需要有以下几个步骤:
1、生成一个远程接口
2、实现远程对象(服务器端程序)
3、生成占位程序和骨干网(服务器端程序)
4、编写服务器程序
5、编写客户程序
6、注册远程对象
7、启动远程对象


四:一个简单应用
一共有三个java类,远程接口,服务端程序,客户端程序

远程接口:

import java.rmi.*;

public interface HelloIn extends java.rmi.Remote{
String sayHello() throws RemoteException;
}


服务端程序:

import java.rmi.*;
import java.net.*;
import java.rmi.registry.*;
import java.rmi.server.*;

public class Hello extends java.rmi.server.UnicastRemoteObject implements HelloIn{
public Hello() throws RemoteException{
super();
}
public String sayHello() throws RemoteException{
return "Hello,World!";
}
public static void main(String[] args){
//System.setSecurityManager(new java.rmi.RMISecurityManager());
try{

Hello h=new Hello();
java.rmi.Naming.rebind("hello",h);
System.out.print("Ready......");
}
catch(Exception e){
e.printStackTrace();
}

}
}

rmic Hello 生成Stub 和 Skeleton
start rmiregistry 执行服务端程序前在命令行方式下启动rmi的注册程序
java Hello 启动服务器

客户端程序:

import java.rmi.*;
import java.rmi.registry.*;

public class Helloworld{
public static void main(String[] args){
//System.setProperty( "java.security.policy", "client.policy" );
//System.setSecurityManager(new java.rmi.RMISecurityManager());
try{
HelloIn hi=(HelloIn)Naming.lookup("//127.0.0.1/hello");
for(int i=0;i<10;i++){
System.out.println(hi.sayHello());
}
}
catch(Exception e){
e.printStackTrace();
}
}
}
编译:javac Helloworld.java
最后执行java Helloworld 控制台打印出 Hello,World,成功调用.
Java RMIRemote Method Invocation)是一种允许在不同 Java 虚拟机(JVM)之间进行远程方法调用的机制,其底层实现依赖于动态代理技术。通过动态代理,RMI 能够让客户端像调用本地方法一样调用远程服务端的方法,而无需显式处理网络通信细节。 ### 动态代理在 RMI 中的作用 在 Java RMI 中,客户端通过代理对象与远程服务进行交互。这个代理对象由 `java.lang.reflect.Proxy` 类生成,并且实现了与远程接口相同的接口。当客户端调用代理对象的方法时,该调用会被转发到一个 `InvocationHandler` 实现类中,从而将方法调用封装为远程请求发送至服务端。 这种机制的核心在于: - **代理类的动态生成**:RMI 在运行时生成代理类,确保客户端可以透明地调用远程方法。 - **方法调用拦截**:所有对代理对象的方法调用都会被 `InvocationHandler` 拦截,进而转换为网络请求。 - **远程通信抽象化**:通过代理和处理器的配合,隐藏了底层网络传输、序列化、反序列化等复杂性 [^1]。 ### RMI 的核心组件及工作流程 Java RMI 主要包括以下几个关键组件: - **远程接口(Remote Interface)**:定义远程服务的方法,必须继承 `java.rmi.Remote` 接口,且每个方法需声明抛出 `RemoteException`。 - **远程实现类(Remote Implementation)**:具体实现远程接口的服务端类。 - **RMI 注册中心(Registry)**:用于注册和查找远程对象引用的服务。 - **动态代理对象**:客户端通过代理对象发起远程方法调用 [^3]。 #### 工作流程简述 1. 服务端启动 RMI 注册中心并绑定远程对象。 2. 客户端通过 `Naming.lookup()` 或 `Registry.getRegistry()` 获取远程接口的代理对象。 3. 客户端调用代理对象的方法,触发 `InvocationHandler` 的 `invoke()` 方法。 4. `invoke()` 方法方法调用信息序列化后发送至服务端。 5. 服务端接收请求,执行真实对象的方法并将结果返回给客户端 [^4]。 ### 示例代码 以下是一个基于 JDK 动态代理实现的简化版 RMI 远程调用示例: ```java import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.*; import java.io.*; // 定义远程接口 interface RemoteService { String sayHello(String name) throws Exception; } // 实现类 class RemoteServiceImpl implements RemoteService { public String sayHello(String name) { return "Hello, " + name; } } // 自定义 InvocationHandler 实现远程调用逻辑 class RemoteInvocationHandler implements InvocationHandler { private String host; private int port; public RemoteInvocationHandler(String host, int port) { this.host = host; this.port = port; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try (Socket socket = new Socket(host, port); ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); ObjectInputStream in = new ObjectInputStream(socket.getInputStream())) { // 发送方法名、参数类型和参数值 out.writeUTF(method.getName()); out.writeObject(method.getParameterTypes()); out.writeObject(args); // 接收返回结果 return in.readObject(); } } } // 简单服务器端监听请求 class SimpleServer { public static void start(RemoteService service, int port) throws IOException { ServerSocket serverSocket = new ServerSocket(port); System.out.println("Server started on port " + port); while (true) { Socket socket = serverSocket.accept(); new Thread(() -> handleClient(socket, service)).start(); } } private static void handleClient(Socket socket, RemoteService service) { try (ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream())) { String methodName = in.readUTF(); Class<?>[] paramTypes = (Class<?>[]) in.readObject(); Object[] args = (Object[]) in.readObject(); Method method = service.getClass().getMethod(methodName, paramTypes); Object result = method.invoke(service, args); out.writeObject(result); } catch (Exception e) { e.printStackTrace(); } } } // 测试类 public class RMIDemo { public static void main(String[] args) throws Exception { // 启动服务端 new Thread(() -> { try { SimpleServer.start(new RemoteServiceImpl(), 8080); } catch (IOException e) { e.printStackTrace(); } }).start(); Thread.sleep(1000); // 等待服务启动 // 创建代理对象 RemoteService proxy = (RemoteService) Proxy.newProxyInstance( RMIDemo.class.getClassLoader(), new Class[]{RemoteService.class}, new RemoteInvocationHandler("localhost", 8080) ); // 调用远程方法 String response = proxy.sayHello("World"); System.out.println(response); } } ``` 输出结果: ``` Server started on port 8080 Hello, World ``` 上述代码演示了如何利用 JDK 动态代理模拟 RMI远程调用过程,其中 `RemoteInvocationHandler` 负责将方法调用序列化并通过网络发送至服务端。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值