【Java】RMI框架搭建

来源:互联网 发布:怎么在mac上装windows 编辑:程序博客网 时间:2024/06/15 07:02

RMI(Remote Method Invoke,即远程方法调用),RMI应用程序包含两个分离的程序,服务端和客户端。典型的服务端程序会创建一些可以访问的远程对象,并等待客户端调用这些对象上的方法。典型的客户端程序获取服务器上一个或多个远程对象的远程引用,然后调用它们上的方法。RMI提供了服务器和客户端通信和来回传递信息的机制。这种应用有时被称为分布式对象应用程序。

下面的插图描述了一个RMI分布式应用程序,它使用RMI注册表来获取对远程对象的引用。服务器调用注册表将名称与远程对象关联(或绑定)。客户端通过服务器注册表中的名称查找远程对象,然后调用它上的方法。另外,RMI系统还可以使用现有Web服务器从服务器到客户机以及从客户端到服务器定义类定义,以便在需要时使用对象。
这里写图片描述


第一章 RMI程序实现(Oracle官方文档)


一、服务端程序

1.1 Remote 对象

创建一个Compute 接口,继承自Remote。

package compute;import java.rmi.Remote;import java.rmi.RemoteException;public interface Compute extends Remote {    <T> T executeTask(Task<T> t) throws RemoteException;}

创建一个Task接口

package compute;public interface Task<T> {    T execute();}

1.2 Remote接口实现

package engine;import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;import java.rmi.server.UnicastRemoteObject;import compute.Compute;import compute.Task;public class ComputeEngine implements Compute {    public ComputeEngine() {        super();    }    public <T> T executeTask(Task<T> t) {        return t.execute();    }    public static void main(String[] args) {//security control        if (System.getSecurityManager() == null) {              System.setSecurityManager(new SecurityManager());        }        try {            String name = "Compute";    //Remote Object’s registry name            Compute engine = new ComputeEngine();//Making the Remote Object Available to Clients            Compute stub =                (Compute) UnicastRemoteObject.exportObject(engine, 0); //The code then adds the name to the RMI registry running on the server            Registry registry = LocateRegistry.getRegistry();            registry.rebind(name, stub);            System.out.println("ComputeEngine bound");        } catch (Exception e) {            System.err.println("ComputeEngine exception:");            e.printStackTrace();        }    }}

二、客户端程序

package client;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;import java.math.BigDecimal;import compute.Compute;public class ComputePi {    public static void main(String args[]) {        if (System.getSecurityManager() == null) {            System.setSecurityManager(new SecurityManager());        }        try {            String name = "Compute";            Registry registry = LocateRegistry.getRegistry(args[0]);            Compute comp = (Compute) registry.lookup(name);            Pi task = new Pi(Integer.parseInt(args[1]));            BigDecimal pi = comp.executeTask(task);            System.out.println(pi);        } catch (Exception e) {            System.err.println("ComputePi exception:");            e.printStackTrace();        }    }    }
package client;import compute.Task;import java.io.Serializable;import java.math.BigDecimal;public class Pi implements Task<BigDecimal>, Serializable {    private static final long serialVersionUID = 227L;    /** constants used in pi computation */    private static final BigDecimal FOUR =        BigDecimal.valueOf(4);    /** rounding mode to use during pi computation */    private static final int roundingMode =         BigDecimal.ROUND_HALF_EVEN;    /** digits of precision after the decimal point */    private final int digits;    /**     * Construct a task to calculate pi to the specified     * precision.     */    public Pi(int digits) {        this.digits = digits;    }    /**     * Calculate pi.     */    public BigDecimal execute() {        return computePi(digits);    }    /**     * Compute the value of pi to the specified number of      * digits after the decimal point.  The value is      * computed using Machin's formula:     *     *          pi/4 = 4*arctan(1/5) - arctan(1/239)     *     * and a power series expansion of arctan(x) to      * sufficient precision.     */    public static BigDecimal computePi(int digits) {        int scale = digits + 5;        BigDecimal arctan1_5 = arctan(5, scale);        BigDecimal arctan1_239 = arctan(239, scale);        BigDecimal pi = arctan1_5.multiply(FOUR).subtract(                                  arctan1_239).multiply(FOUR);        return pi.setScale(digits,                            BigDecimal.ROUND_HALF_UP);    }    /**     * Compute the value, in radians, of the arctangent of      * the inverse of the supplied integer to the specified     * number of digits after the decimal point.  The value     * is computed using the power series expansion for the     * arc tangent:     *     * arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 +      *     (x^9)/9 ...     */       public static BigDecimal arctan(int inverseX,                                     int scale)     {        BigDecimal result, numer, term;        BigDecimal invX = BigDecimal.valueOf(inverseX);        BigDecimal invX2 =             BigDecimal.valueOf(inverseX * inverseX);        numer = BigDecimal.ONE.divide(invX,                                      scale, roundingMode);        result = numer;        int i = 1;        do {            numer =                 numer.divide(invX2, scale, roundingMode);            int denom = 2 * i + 1;            term =                 numer.divide(BigDecimal.valueOf(denom),                             scale, roundingMode);            if ((i % 2) != 0) {                result = result.subtract(term);            } else {                result = result.add(term);            }            i++;        } while (term.compareTo(BigDecimal.ZERO) != 0);        return result;    }}

三、编译项目

假设我项目的项目路径是D:\workspace\RMIServer,RMIServer是项目名称。

3.1 打jar包

cd D:/workspace/RMIServer/srcjavac compute/Compute.java compute/Task.javajar cvf compute.jar compute/*.class

将打包好的jar包,放到一个固定的目录,如C:/Users/SWL/public_html/classes4。这个jar是服务端和客户端共用的,客户端在向服务端发送请求时,必须要获取这个jar,不然会报ClassNotFound的错误。

3.2 编译服务端

cd D:/workspace/RMIServer/srcjavac -cp C:/Users/SWL/public_html/classes4/compute.jar    Engine/ComputeEngine.java

编译时一定要指向事先打包好的jar文件。

3.3 编译客户端

cd D:/workspace/RMIServer/srcjavac -cp C:/Users/SWL/public_html/classes4/compute.jar    client/ComputePi.java client/Pi.javamkdir C:/Users/SWL/public_html/classes4/clientcp client\Pi.class    C:/Users/SWL/public_html/classes4/client

同样,编译时一定要指向事先打包好的jar文件。


四、运行项目

4.1 安全策略

在运行项目之前,必须先设置安全策略。服务端和客户端在运行之前都是要设置安全策略。
在项目的src目录下创建文件windows.policy,codebase路径指向项目路径。

grant codeBase "file:/D:/workspace/RMIServer/src" {    permission java.security.AllPermission;};

4.2 启动rmi的注册监听服务

Jdk的bin目录下有自带的rmiregistry.exe。在启动服务端之前必须先启动rmi的注册服务。rmiregistry服务默认端口为1099,可以自定义端口

cd D:/workspace/RMIServer/srcstart rmiregistry [2001]

4.3 启动服务

java -cpD:/workspace/RMIServer/src;C:/Users/SWL/public_html/classes4/compute.jar-Djava.rmi.server.codebase=file:/C:/Users/SWL/public_html/classes4/compute.jar     -Djava.rmi.server.hostname=127.0.0.1     -Djava.security.policy=windows.policy        engine.ComputeEngine

启动服务时,一定要指定java项目自身src目录和之前打的jar包地址。另外,也要指定java.rmi.server.codebase、java.rmi.server.hostname、java.security.policy这些系统环境变量。

正常情况下,会有“ComputeEngine bound”的日志打出,此时Server服务正常启动。
如有报错,请仔细检查上述流程。一般报错类型为ClassNotFound或者checkPermisson,前者,检查codebase是否无误,后者检查windows.policy(存放路径、内容)文件是否无误。

4.4 启动客户端

java -cp D:/workspace/RMIServer/src;C:/Users/SWL/public_html/classes4/compute.jar-Djava.rmi.server.codebase=file:/C:/Users/SWL/public_html/classes4/compute.jar     -Djava.security.policy=windows.policyclient.ComputePi 127.0.0.1 45

与服务端一样,启动时也要指定启动环境,同时指定系统环境变量。
正常情况下,会有一行输出:

3.141592653589793238462643383279502884197169399

五、总结

在单纯的java项目中,现在是完全没问题的。可以将这份代码及其拷贝随便放置,只要有一个服务端启动,其它的项目中的客户端都可以实现访问。但是,在java web项目想要实现客户端连接服务端,仅仅这些还是不够的。


第二章 Java Web中的实现

在上面的基础下,需要做部分修改。
在原有的ComputePi.java和ComputeEngine.java的main函数中需要加入系统环境变量设置,如下示:

String os = System.getProperty("os.name");if (os.length() > 0 && os.toLowerCase().contains("window")) {    System.setProperty("java.rmi.server.codebase","file:/c:/Users/User/public_html/classes4/compute.jar");    System.setProperty("java.security.policy","windows.policy");} else {    System.setProperty("java.rmi.server.codebase","file:///home//RMI/RService.jar");    System.setProperty("java.security.policy","linux.policy");}System.setProperty("java.rmi.server.hostname","127.0.0.1");

这部分是设置java.rmi.server.codebase、java.security.policy、java.rmi.server.hostname环境变量。


第三章Linux环境

Linux环境与windows环境的配置方式基本一致,但是有需要特别注意:

1.修改jdk默认的安全策略文件。

/usr/java/jdk1.7.0_71/jre/lib/security/java.policy,在grant中添加:

permission java.security.AllPermission; permission java.util.PropertyPermission "/home/*", "read";permission java.io.FilePermission "/home/*", "read,write";permission java.lang.RuntimePermission "accessClassInPackage.sun.jdbc.odbc";

注意:read、write授权时,指向项目所在的路径。
Windows环境下可能有时也需要添加。

2.在src目录下启动rmiregistry服务。

这个一定要注意,一定要在项目的src目录下启动rmi的注册服务,不然会报错。

参考文档:

  1. Oracle官方指导文档:https://docs.oracle.com/javase/tutorial/rmi/overview.html
  2. 安全策略相关:
    https://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html
    https://docs.oracle.com/javase/7/docs/technotes/guides/security/permissions.html
    https://docs.oracle.com/javase/1.5.0/docs/guide/security/index.html

源码+文档

点击这里,下载源码

原创粉丝点击