java-RMI

定义

RMI:远程方法调用(Remote Method Invocation),是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口.它使得客户机上的运行的程序能够调用远程服务器上的对象的对象.此远程调用特性就是为了能够在网络环境中分布操作.RMI就是简化了远程调用.

RPC:远程过程调用,是在分布式中常用到的一个协议,此协议就是能够让调用远程对象如同调用本地对象一样,RMI就是其实现方式的一种,当然了,还有很多中实现方式,比如说rest方式等等.现在主要说一下RMI方式;

原理

RMI主要由三个部分组成:

  • registry(JDK提供的一个可独立运行的程序)
  • server程序,对外提供远程调用的服务对象
  • client 调用server提供的服务

首先,先启动registry服务,启动时可以指定服务监听的端口,也可以使用默认的端口(1099)

其次,server端在本地先实例化一个提供服务的实现类,然后通过RMI提供的Naming/Context/Registry(下面实例用的Registry)等类的bind或rebind方法将刚才实例化好的实现类注册到registry上并对外暴露一个名称。

最后,client端通过本地的接口和一个已知的名称(即registry暴露出的名称)再使用RMI提供的Naming/Context/Registry等类的lookup方法从RMIService那拿到实现类,其实是RMI系统生成的stub存根代理。这样虽然本地没有这个类的实现类,但所有的方法都在接口里了,便可以实现远程调用对象的方法了.


通信过程大致是这样,server端提供服务后会有一个skeleton骨干网,client调用者调用lookup方法会返回一个存根,通过此存根与server通信,调用远程的方法. 方法经过存根(stub),远程调用层,再到传输层,最后在到skeleton,通过skeleton调用实例方法,之后在将结果按照原路返回.
而这个stub和skeleton代理都是由RMI系统动态生成的.服务端只需要继承UnicastRemoteObject接口就可以了.这些底层细节是不需要我们去关心的.这就是它的强大之处.

实例:

首先必须有一个暴露的接口,这个接口在server和client中都得有,因为需要这个接口去调用方法

1
2
3
4
5
6
7
8
9
10
11
12
import com.spj.rmidemo.bean.User;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface UserManage extends Remote {

String getUserName()throws RemoteException;

User getUser() throws RemoteException;

}

其次得server实现这个接口的具体方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 这里需要继承UnicastRemoteObject类,来允许jvm创建存根和代理
public class UserManageImpl extends UnicastRemoteObject implements UserManage {

protected UserManageImpl() throws RemoteException {
}

@Override
public String getUserName() throws RemoteException {
return "spj";
}

@Override
public User getUser()throws RemoteException {
return new User("spj","123",21);
}
}

这里就可以使用一个方法来暴露这个服务

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) throws Exception {

// 所要暴露的对象
UserManage userManage = new UserManageImpl();

// 创建一个本地的注册中心,绑定端口2002
Registry registry = LocateRegistry.createRegistry(2002);
// 这里能够使用Naming.rebind绑定对象或者使用registry.bind()暴露对象
//Naming.rebind("rmi://127.0.0.1:2002/UserManageService",userManage);
registry.bind("userManage",userManage);
System.out.println("server is ready!");
}

服务端的程序就写好了,而客户端可以没有这个实现类,也就是没有接口的实现类,就可以调用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
try {
Registry registry = LocateRegistry.getRegistry(2002);

// lookup方法参数为服务器绑定是设置的访问名称.
UserManage userManage = (UserManage)registry.lookup("userManage");
// 这里可以向调用本地方法一样调用远程方法.
System.out.println(userManage.getUser());
}catch (Exception e){
e.printStackTrace();
}
}

输出:
User{name='spj', psss='123', age=21}

参考:

https://blog.csdn.net/qq_28081453/article/details/83279066
https://blog.csdn.net/xinghun_4/article/details/45787549