Java RMI的简单使用

Java中的RMI即远程方法调用(Remote Method Invocation)是Java用于分布式应用开发的API,作为一种解决方案,它大大加强了Java在分布式开发上的能力。通过使用RMI,使得我们在客户端的电脑上调用远程方法就和调用本地方法感觉是一样的,底层的具体实现处理是对用户透明的,用户感觉不到在本地JVM上调用远程服务器上JVM上的程序。

使用RMI时,与本地的类直接打交道的其实是一个stub,它相当于客户端对象的一个代理。而远程服务器上接收客户端请求处理的其实是服务器上的一个skeleton,这是服务器对象的一个代理。当然,这些我们不用操心,它们都是自动生成的。

第一步,先新建一个远程接口

使用RMI导入的类主要都在 java.rmi 包里。这里我们建立的自己的接口拓展自Remote接口。声明两个方法,一个用以传输服务器的消息,一个通过远程方法调用在服务器端打印日志,而且声明的所有方法都要抛出RemoteException异常。

1
2
3
4
public interface MyRemote extends Remote {
String transMsg() throws RemoteException;
void printLog() throws RemoteException;
}

第二步,实现接口

实现 MyRemote 接口还不够,我们还要继承 UnicastRemoteObject 类,从而成为远程服务对象。继承 UnicastRemoteObject 后,我们的类构造方法必须要抛出一个 RemoteException。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Server extends UnicastRemoteObject implements MyRemote {
public Server() throws RemoteException {
}
public String transMsg() {
return "This is from server!";
}
@Override
public void printLog() {
System.out.println("Client invoke!");
}
}

另外,还有一点,远程方法的返回值传输的如果是一个类,必须是可序列化的,要实现Serializable 接口。

第三步,注册RMI服务

我们想让客户端调用服务器的方法,就必须在服务器实例化一个远程服务,然后注册到RMI Registry里。LocateRegistry 的 createRegistry() 方法,可创建一个Registry实例,通过传入一个参数可指定端口。通过 Naming.rebind() 方法可绑定一个实例对象。这里要注意的是,我们实际上注册的是一个stub。

在服务器上运行Test时,要先在生成的 class 文件的目录中运行 rmiregistry 命令,否则会报找不到类的错。比如这里是 ~/JavaProject/out/production/JavaProject/proxy, 就要cd 到该目录,然后运行 rmiregistry(本文代码编译运行的操作系统为Linux,IDE 为IntelliJ Idea)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test {
public static void main(String []args) {
MyRemote server = null;
try {
server = new Server();
LocateRegistry.createRegistry(8081);
Naming.rebind("/ShitObject", server);//传入一任意一个名称,和实例化的对象
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}

客户端代码

使用Naming.lookup() 查找已经注册的远程服务对象。lookup() 方法返回一个Object 对象,获得对象后转换成 Server 类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Client {
public Client() throws RemoteException, NotBoundException, MalformedURLException {
}
public static void main(String args[]) {
MyRemote server = null;
try {
server = (MyRemote)Naming.lookup("rmi://127.0.0.1/ShitObject");
System.out.println(server.transMsg());
server.printLog();
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
// 客户端打印:This is from server!
//服务器端打印:Client invoke!