Java RMI 入门 - [无坑篇]


Introduction

JavaRMI用于不同虚拟机之间的通信,这些虚拟机可以在不同的主机上、也可以在同一个主机上;一个虚拟机中的对象调用另一个虚拟上中的对象的方法,只不过是允许被远程调用的对象要通过一些标志加以标识。这样做的特点如下:

优点:避免重复造轮子; 缺点:调用过程很慢,而且该过程是不可靠的,容易发生不可预料的错误,比如网络错误等;

在RMI中的核心是远程对象(remote object),除了对象本身所在的虚拟机,其他虚拟机也可以调用此对象的方法,而且这些虚拟机可以不在同一个主机上。每个远程对象都要实现一个或者多个远程接口来标识自己,声明了可以被外部系统或者应用调用的方法(当然也有一些方法是不想让人访问的)。

从网上阅读了无数rmi demo和 paper,存在各种各样的坑,搞了一下午,终于填完,下方代码保证能够运行:


GreetService.java 接口文件:

1
2
3
4
5
6
7
package com.jd.rmidemo.service;

import java.rmi.RemoteException;

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

GreetServiceImpl.java 具体实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package com.jd.rmidemo.service;

import java.rmi.RemoteException;

public class GreetServiceImpl extends java.rmi.server.UnicastRemoteObject implements GreetService {

    private static final long serialVersionUID = 3434060152387200042L;

    public GreetServiceImpl() throws RemoteException {
        super();
    }

    @Override
    public String sayHello(String name) throws RemoteException {
        return "Hello " + name;
    }

}

Server.java   远程部署的RMI Server文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.jd.rmidemo.main;

import com.jd.rmidemo.service.GreetServiceImpl;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Server {

    public static void main(String[] args) {
        try {
            // 2333是RMI Service监听的端口
            Registry registry = LocateRegistry.createRegistry(2333);
            registry.bind("test",  new GreetServiceImpl());
            System.out.println("server is ready");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Client.java 连接文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.jd.rmidemo.main;

import com.jd.rmidemo.service.GreetService;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Client {

    public static void main(String[] args) {
        try {

            Registry registry = LocateRegistry.getRegistry("Server服务器IP",2333);

            // 注册列表
            for(String v : registry.list()){
                System.out.println(v);
            }

            GreetService greetService = (GreetService)registry.lookup("test");  //注:通过接口拿
            System.out.println(greetService.sayHello("Jobs"));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

为什么网上很多demo都跑不成功呢? 其中最大的问题是hostname原因,因为:


客户端程序向服务端请求一个对象的时候,返回的stub对象里面包含了服务器的hostname,客户端的后续操作根据这个hostname来连接服务器端。 网上执行rmiregistry命令 起到的作用只是为了方便client获取到stub对象,如果还有其他的方法让client拿到stub就不需要RMIRegistry 了,因为client一旦拿到了stub就不需要RMIRegistry了,如上,我们的LocateRegistry.createRegistry就已经完成这步骤操作了。

如果你的Server.java部署在远程服务器,那么需要手动指定 java.rmi.server.hostname=, 例: java -Djava.rmi.server.hostname=118.24.75.130 -jar rmidemo.jar

参考文章:

  1. https://blog.csdn.net/chenchaofuck1/article/details/51558995/
  2. https://www.jianshu.com/p/2c78554a3f36