java的一个重要特征就是分布式计算. rmi就是一种非常简单的远程调用协议. 它比web service要简单很多. 但现在java的远程调用越来越趋向使用http, 例如httpclient + xml/json + servlet实现更加开放,简单,mvc,轻量的远程调用. 但REST并不是我们这篇文章要讨论的东西, rmi才是, rmi的应用还是广泛的,例如大名鼎鼎的EJB,就是基于rmi实现的.
rmi的架构分server端和client端, server就是服务提供方,client便是索取服务方.
- Server端需要启动一个RMI的注册表服务先,server端可以想注册表注册要开放的调用引用. 而client端则可以从注册表获取要调用的引用.
注册表的启动方法为: java bin目录下,运行命令 rmiregistry. - server端向注册表注册.
- client端通过jndi获取注册表上的引用. 此引用常被称为stub, 存根.
- client调用存根,存根封装了和server端的信息交互, server端的代理封装了信息的解析和对真正引用调用.以上就是 client -- stub ----------- proxy -- server.
rmi调用需要传递值和对象. 其中对象传递分为传递远程对象和传递本地对象.
- 传递本地对象, 就是传递一个实现了Serializable的对象, 对象会被序列化传递到client端.
- 传递远程对象, 则是传递一个对server端的对象的引用, client端在此引用上的操作都将使用rmi的方式回到server端调用. 有点像 rmi 嵌套 rmi.
至于如何运行一个rmi. 分3步走:
- 启动rmiregistry.
- 启动server, 启动server的时候需要给jvm设定codebase参数,告诉rmiregistry去哪下载要注册的interface. 例如: -Djava.rmi.server.codebase=http://localhost:8080/examples/. 想注册表注册服务.
- 启动client, 执行调用.
接下来, 是我的一个实现. 实现中涵盖了以上所有的内容. 首先要贴的代码是server端和client端公用的interface与交互类.
package remote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Compute extends Remote {
public int sum(int a, int b) throws RemoteException;
public Result mult(int a, int b) throws RemoteException;
public RemoteExec getRemoteExec() throws RemoteException;
}
package remote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteExec extends Remote {
public int exec(int a, int b) throws RemoteException;
public Result execResult(int a, int b) throws RemoteException;
}
package remote;
import java.io.Serializable;
public class Result implements Serializable {
private static final long serialVersionUID = -4779724309418083812L;
private int result;
public Result(int r) {
this.result = r;
}
@Override
public String toString() {
return ""+result;
}
}
以上3个类是client和server共有的. 两个实现了remote的interface需要放在一个共享目录,一共注册表能加载到他们.
然后是server端的实现.
package remote;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class Server {
/**
* @param args
* @throws RemoteException
* @throws NamingException
*/
public static void main(String[] args) throws RemoteException, NamingException {
//if (System.getSecurityManager() == null) {
//System.setSecurityManager(new SecurityManager());
//}
Compute c = new Compute() {
@Override
public int sum(int a, int b) throws RemoteException {
// TODO Auto-generated method stub
return a+b;
}
@Override
public Result mult(int a, int b) throws RemoteException {
// TODO Auto-generated method stub
return new Result(a*b);
}
@Override
public RemoteExec getRemoteExec() throws RemoteException {
// TODO Auto-generated method stub
RemoteExec re = new RemoteExec() {
@Override
public int exec(int a, int b) {
// TODO Auto-generated method stub
return a-b;
}
@Override
public Result execResult(int a, int b) {
// TODO Auto-generated method stub
return new Result(a*b);
}
};
RemoteExec stub = (RemoteExec) UnicastRemoteObject.exportObject(re, 0);
return stub;
}
};
Compute stub = (Compute) UnicastRemoteObject.exportObject(c, 0);
//Registry registry = LocateRegistry.getRegistry();
//registry.rebind(name, stub);
Context namingCtx = new InitialContext();
namingCtx.rebind("rmi:compute", stub);
System.out.println("ComputeEngine bound");
}
}
最后是client端的实现.
package remote;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class Client {
/**
* @param args
* @throws RemoteException
* @throws NotBoundException
* @throws NamingException
*/
public static void main(String[] args) throws RemoteException, NotBoundException, NamingException {
Context ctx = new InitialContext();
String url = "rmi://localhost/compute";
Compute comp = (Compute) ctx.lookup(url);
System.out.println(comp.sum(1, 3));
System.out.println(comp.mult(2, 3));
System.out.println(comp.getRemoteExec().exec(100, 12));
System.out.println(comp.getRemoteExec().execResult(100, 6));
}
}
跑一下吧.