Monday, February 11, 2013

分布式调用rmi


分布式调用rmi

from 
http://my.oschina.net/xpbug/blog/108494


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

No comments:

Post a Comment