Elcker-Java RMI简介及使用说明
RMI 简介
RMI (Remote Method Invocation) 远程方法调用,可以认为是RPC的Java版本,使用 RMI 技术可以使一个Java 虚拟机中的对象,调用另一个Java 虚拟机中的对象方法并获取调用结果。使用的是JRMP(Java Remote Messaging Protocol)协议,JRMP是专门为Java定制的通信协议,所以是纯Java的分布式解决方案,java RMI封装了远程调用的实现细节,进行简单的配置之后,就可以如同调用本地方法一样,比较透明地调用远端方法。
RMI的核心概念
Registry: 提供服务注册与服务获取。即Server端向Registry注册服务,比如地址、端口等一些信息,Client端从Registry获取远程对象的一些信息,如地址、端口等,然后进行远程调用。
Server: 远程方法的提供者,并向Registry注册自身提供的服务
Client: 远程方法的消费者,从Registry获取远程方法的相关信息并且调用
RMI 的API说明
Remote
一个interface,这个interface中没有声明任何方法。只有定义在“remote interface",即继承了Remote的接口中的方法,才可以被远程调用。
RemoteException
RemoteException是所有在远程调用中所抛出异常的超类,所有能够被远程调用的方法声明,都需要抛出此异常
Naming
提供向注册中心保存远程对象引用或者从注册中心获取远程对象引用的方法。这个类中的方法都是静态方法,每一个方法都包含了一个类型为String的name参数, 这个参数是URL格式,形如://host:port/name
Registry
一个interface, 其功能和Naming类似,每个方法都有一个String类型的name参数,但是这个name不是URL格式,是远程对象的一个命名。Registry的实例可以通过方法LocateRegistry.getRegistry()获得
LocateRegistry
用于获取到注册中心的一个连接,这个连接可以用于获取一个远程对象的引用。也可以创建一个注册中心。
RemoteObject
重新覆写了Object对象中的equals,hashCode,toString方法,从而可以用于远程调用
UnicastRemoteObject
用于RMI Server中导出一个远程对象并获得一个stub。这个stub封装了底层细节,用于和远程对象进行通信。
Unreferenced
一个interface, 声明了方法:void unreferenced()如果一个远程对象实现了此接口,则这个远程对象在没有任何客户端引用的时候,这个方法会被调用。
RMI的工作原理
Demo
RMI Server
1.定义传输对象,此对象需要实现系列化接口
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = 6490921832856589236L;
private String name;
private Integer age;
private String skill;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
@Override
public String toString() {
return "User{" +
"name='" + name + ''' +
", age=" + age +
", skill='" + skill + ''' +
'}';
}
}
2.定义服务器接口(需要继承 Remote 类,方法需要抛出 RemoteException)。
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface UserService extends Remote {
/**
* 查找用户
*
* @param userId
* @return
* @throws RemoteException
*/
User findUser(String userId) throws RemoteException;
}
3.实现服务器接口(需要继承 UnicastRemoteObject 类,实现定义的接口)。
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class UserServiceImpl extends UnicastRemoteObject implements UserService {
protected UserServiceImpl() throws RemoteException {
}
@Override
public User findUser(String userId) throws RemoteException {
// 加载在查询
if ("00001".equals(userId)) {
User user = new User();
user.setName("金庸");
user.setAge(100);
user.setSkill("写作");
return user;
}
throw new RemoteException("查无此人");
}
}
4.注册( rmiregistry)远程对象,并启动服务端程序。
服务端绑定了 UserService 对象作为远程访问的对象,启动时端口设置为 1900
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
public class RmiServer {
public static void main(String[] args) {
try {
UserService userService = new UserServiceImpl();
LocateRegistry.createRegistry(1900);
// Naming.bind("rmi://localhost:1900/user", userService);
Naming.rebind("rmi://localhost:1900/user", userService);
System.out.println("start server,port is 1900");
} catch (Exception e) {
e.printStackTrace();
}
}
}
RMI Client
import java.rmi.Naming;
import com.wdbyte.rmi.server.User;
import com.wdbyte.rmi.server.UserService;
public class RmiClient {
public static void main(String args[]) {
User answer;
String userId = "00001";
try {
// lookup method to find reference of remote object
UserService access = (UserService)Naming.lookup("rmi://localhost:1900/user");
answer = access.findUser(userId);
System.out.println("query:" + userId);
System.out.println("result:" + answer);
} catch (Exception ae) {
System.out.println(ae);
}
}
}
扩展
LipeRMI rmi 的代替方案