前言
前段时间查代码问题时发现应用代码中接口变量指向的实例对象是Naming.lookup(xx) 返回的,但在整个应用里又找不到这个接口的实现,当时就特别好奇,后面才了解到原来就是远程调用(RMI),今天就简单了解了一下。
RMI :远程方法调用(Remote Method Invocation)。能够让在某个java虚拟机上的对象像调用本地对象一样调用另一个java 虚拟机中的对象上的方法
RMI远程调用步骤:
1,客户对象调用客户端辅助对象上的方法
2,客户端辅助对象打包调用信息(变量,方法名),通过网络发送给服务端辅助对象
3,服务端辅助对象将客户端辅助对象发送来的信息解包,找出真正被调用的方法以及该方法所在对象
4,调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象
5,服务端辅助对象将结果打包,发送给客户端辅助对象
6,客户端辅助对象将返回值解包,返回给客户对象
7,客户对象获得返回值
代码实现
package com.rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
//创建远程方法接口,该接口必须继承自Remote接口
//Remote 接口是一个标识接口,用于标识所包含的方法可以从非本地虚拟机上调用的接口
public interface Hello extends Remote {
//由于远程方法调用的本质依然是网络通信,只不过隐藏了底层实现,网络通信是经常会出现异常的,
//所以接口的所有方法都必须抛出RemoteException以说明该方法是有风险的
public String sayHello(String name) throws RemoteException;
}
package com.rmi;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
//实际传输的时候就是这个类的实例,所以要指定 serialVersionUID ,不想序列化的字段加关键词 transient即可
public class HelloImpl extends UnicastRemoteObject implements Hello {
private static final long serialVersionUID = 1L;
public HelloImpl() throws RemoteException {
}
@Override
public String sayHello(String name) throws RemoteException {
return "Hello," + name;
}
}
package com.rmi.test;
import com.rmi.Hello;
import com.rmi.HelloImpl;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMISocketFactory;
public class ServerTest {
public static void main(String[] args) {
test2();
}
//在jdk的bin下有个rmiregistry.exe 需要在项目的classpath下(比如target/classes下)运行该程序
//rmiregistry 1099 (这个是默认端口) 可以使用 start rmiregistry 命令在后台运行
private static void test1(){
try {
Hello hello = new HelloImpl();
Naming.rebind("user", hello);
System.out.println("rmi server is ready ...");
} catch (RemoteException | MalformedURLException e) {
e.printStackTrace();
}
}
//不再通过rmiregistry单独运行 通过编程来实现
private static void test2(){
try {
Hello hello = new HelloImpl();
//注册服务
Registry registry = LocateRegistry.createRegistry(1099);
registry.rebind("user", hello);
System.out.println("rmi server is ready ...");
} catch (RemoteException e ) {
e.printStackTrace();
}
}
}
客户端调用(建议另外启一个项目,这样模拟更真实)
先写一个相同的接口,接口的全限量名要和服务端的保持一致
package com.rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* @Description: 远程调用
* @Author: xiaoaa
* @Date: 2020/12/12
*/
public interface Hello extends Remote {
public String sayHello(String name) throws RemoteException;
}
测试代码
```java
package com.rmi;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class RmiTest {
public static void main(String[] args) {
test2();
}
private static void test1(){
try {
Hello hello = (Hello) Naming.lookup("user");
System.out.println(hello.sayHello("xiao"));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NotBoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void test2(){
try {
Hello hello = (Hello) Naming.lookup("rmi://127.0.0.1:1099/user");
System.out.println(hello.sayHello("xiao"));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NotBoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
写得不好的地方,请见谅。大家有踩到坑的也欢迎分享,谢谢。