Remote Compute Engine——JAVA RMI 学习二

Remote Compute Engine——JAVA RMI

什么是RMI:

The Java Remote Method Invocation (RMI) system allows an object
running in one Java virtual machine to invoke methods on an object
running in another Java virtual machine. RMI provides for remote
communication between programs written in the Java programming
language.

RMI是一种可以是使一个JVM里的对象去掉用另一台JVM里对象的技术!
RMI也被称作分布式对象应用。接下来我用一个实例介绍:

RMI——Distributed object applications

Distributed object applications need to do the following:

  • 服务端,注册对象并暴漏引用:

The server calls the registry to associate (or bind) a name with a
remote object.

  • 客户端调用:

接下来以一个分布式计算程序为例:
首先, 定义一个远程接口,作为远程虚拟机“通讯”的协议
Compute.java



package compute;

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


//By extending the interface java.rmi.Remote,
//the Compute interface identifies itself as an interface whose methods can be invoked from another Java virtual machine.
//Any object that implements this interface can be a remote object.
public interface Compute extends Remote {
    // the executeTask method is a remote method

    // This exception is thrown by the RMI system from a remote method
    // invocation to indicate that either a communication failure or a protocol
    // error has occurred.

    // A RemoteException is a checked exception, so any code invoking a remote
    // method needs to handle this exception by either catching it or declaring
    // it in its throws clause.
     T executeTask(Task t) throws RemoteException;

    // RMI uses the Java object serialization mechanism to transport objects by
    // value between Java virtual machines. For an object to be considered
    // serializable, its class must implement the java.io.Serializable marker
    // interface.

    // Because RMI can assume that the Task objects are written in the Java
    // programming language, implementations of the Task object that were
    // previously unknown to the compute engine are downloaded by RMI into the
    // compute engine's Java virtual machine as needed. This capability enables
    // clients of the compute engine to define new kinds of tasks to be run on
    // the server machine without needing the code to be explicitly installed on
    // that machine
}

远程接口的实现类 ComputeEngine.java


package compute;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

//This declaration states that the class implements the 
//Compute remote interface and therefore can be used for a remote object.
public class ComputeEngine implements Compute {
    // 1.Declare the remote interfaces being implemented
    // 2.Define the constructor for each remote object
    // 3.Provide an implementation for each remote method in the remote
    // interfaces
    public ComputeEngine() {
        super();
    }

    public  T executeTask(Task t) {
        return t.execute();
    }

    public static void main(String[] args) {

        // 1.Create and install a security manager

//       If an RMI program does not install a security manager, RMI will not
//       download classes (other than from the local class path) for objects
//       received as arguments or return values of remote method invocations.
//       This restriction ensures that the operations performed by downloaded
//       code are subject to a security policy.

        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new SecurityManager());
        }
        try {
            // 2.Create and export one or more remote objects

//           - Remote objects are essentially passed by reference. A remote
//           object reference is a stub, which is a client-side proxy that
//           implements the complete set of remote interfaces that the remote
//           object implements.
//           
//           - Local objects are passed by copy, using object serialization.
//           By default, all fields are copied except fields that are marked
//           static or transient. Default serialization behavior can be
//           overridden on a class-by-class basis.

            String name = "Compute";
            Compute engine = new ComputeEngine();

//           Note that the type of the variable stub must be Compute, not
//           ComputeEngine, because the stub for a remote object only
//           implements the remote interfaces that the exported remote object
//           implements.

            Compute stub = (Compute) UnicastRemoteObject.exportObject(engine,
                    0);
            Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);

            // 3.Register at least one remote object with the RMI registry (or
            // with another naming service, such as a service accessible through
            // the Java Naming and Directory Interface) for bootstrapping
            // purposes

//           The system provides a particular type of remote object, the RMI
//           registry, for finding references to other remote objects. The RMI
//           registry is a simple remote object naming service that enables
//           clients to obtain a reference to a remote object by name. The
//           registry is typically only used to locate the first remote object
//           that an RMI client needs to use. That first remote object might
//           then provide support for finding other objects.
//           
//           The java.rmi.registry.Registry remote interface is the API for
//           binding (or registering) and looking up remote objects in the
//           registry. The java.rmi.registry.LocateRegistry class provides
//           static methods for synthesizing a remote reference to a registry
//           at a particular network address (host and port). These methods
//           create the remote reference object containing the specified
//           network address without performing any remote communication.
//           LocateRegistry also provides static methods for creating a new
//           registry in the current Java virtual machine
//           
//           The registry can be shared by all servers running on a host, or
//           an individual server process can create and use its own registry.

            registry.rebind(name, stub);
            System.out.println("ComputeEngine bound");

//            In the parameters and return values of remote method invocations,
//            objects that are not remote objects are passed by value. Thus, a
//            copy of the object is created in the receiving Java virtual
//            machine. Any changes to the object's state by the receiver are
//            reflected only in the receiver's copy, not in the sender's
//            original instance. Any changes to the object's state by the
//            sender are reflected only in the sender's original instance, not
//            in the receiver's copy.

//            When a remote invocation on the registry is made, a stub for the
//            remote object is passed instead of a copy of the remote object
//           itself. Remote implementation objects, such as instances of
//            ComputeEngine, never leave the Java virtual machine in which they
//            were created. Thus, when a client performs a lookup in a server's
//            remote object registry, a copy of the stub is returned. Remote
//            objects in such cases are thus effectively passed by (remote)
//            reference rather than by value.

        } catch (Exception e) {
            System.err.println("ComputeEngine exception:");
            e.printStackTrace();
        }
    }
}
package compute;

public interface Task {
    T execute();
}

接着,是客户端的任务代码。


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;
    }
}

代码就这么多,为什么我把它叫做远程计算引擎呢?你可能一头雾水。

我们首先把Task.java Compute.java 两个接口成jar包,部署到服务端(比如ann的机器)机器上和客户端(比如jone的机器)机器上。服务器,客户端部署树形图:
Remote Compute Engine——JAVA RMI 学习二_第1张图片

启动服务机器,ComputeEngine运行后。便可以使用ComputePi.java,将任务送给服务器,服务器计算完之后,把结构返回给客户端。当然,客户端不止一个,各种不同的实现(也就是业务逻辑完全不同的客户端)的客户端,都可以使用服务器来完成自己的任务计算。
Remote Compute Engine——JAVA RMI 学习二_第2张图片

有个一个困惑是,各个客户端有各种不同的任务实现代码(Task的具体实现),那么服务端怎么知道客户端的代码呢?聪明的你肯定猜到,服务端必须能获取客户端的代码。 这就是CodeBase的作用了。详细流程图如下:

Remote Compute Engine——JAVA RMI 学习二_第3张图片

Remote Compute Engine——JAVA RMI 学习二_第4张图片

按照如上目录结果,我启动的截图:

1.在服务器目录下,启动registry

这里写图片描述

2.启动服务器
这里写图片描述

3.客户端调用远程对象,完成计算任务:

这里写图片描述

官网的命令有一些小错误,让我花了不少时间。写的匆忙,欢迎质疑。演示源码地址:http://download.csdn.net/detail/lemon89/9404528
.

引用参考:

http://docs.oracle.com/javase/6/docs/technotes/guides/rmi/codebase.html

https://docs.oracle.com/javase/tutorial/rmi/

你可能感兴趣的:(RMI)