Monday, January 7, 2013

RMI


Java Remote Method Invocation (Java RMI) enables the programmer to create distributed Java technology-based to Java technology-based applications, in which the methods of remote Java objects can be invoked from other Java virtual machines, possibly on different hosts. RMI uses object serialization to marshal and unmarshal parameters and does not truncate types, supporting true object-oriented polymorphism.
Let’s just say RMI is the “older version” of ejb remote. Because in EJB, enterprise beans are made available for remote clients using RMI.
In this example, im trying to create 2 different project, one is the RMI Server and another one is RMI Client.
First is a simple interface, i will use this interface to create invocation methods and implementations. This interface will be on both Server’s and Client’s side.
1package com.edw.rmi;
2
3import java.rmi.Remote;
4import java.rmi.RemoteException;
5
6public interface Message extends Remote {
7    void sayHello(String name) throws RemoteException;
8}
and next is the implementation for my interface, it is exist only on the server’s side. It has to implements my interface and extends to UnicastRemoteObject class.
01package com.edw.rmi;
02
03import java.rmi.RemoteException;
04import java.rmi.server.UnicastRemoteObject;
05
06public class MessageImpl extends UnicastRemoteObject implements Message {
07
08    public MessageImpl() throws RemoteException {       
09    }
10     
11    @Override
12    public void sayHello(String name) throws RemoteException {
13        System.out.println("hello "+name);
14    }
15     
16}
And next is creating my server side application. Im running on port 1099 for RMI port.
01package com.edw.main;
02
03import com.edw.rmi.MessageImpl;
04import java.rmi.registry.LocateRegistry;
05import java.rmi.registry.Registry;
06
07public class Main {
08     
09    private void startServer(){
10        try {
11            // create on port 1099
12            Registry registry = LocateRegistry.createRegistry(1099);
13             
14            // create a new service named myMessage
15            registry.rebind("myMessage", new MessageImpl());
16        } catch (Exception e) {
17            e.printStackTrace();
18        }     
19        System.out.println("system is ready");
20    }
21     
22    public static void main(String[] args) {
23        Main main = new Main();
24        main.startServer();
25    }
26}
And this is my client’s side application
01package com.edw.main;
02
03import java.rmi.registry.LocateRegistry;
04import java.rmi.registry.Registry;
05import com.edw.rmi.Message;
06
07public class Main {
08     
09    private void doTest(){
10        try {
11            // fire to localhost port 1099
12            Registry myRegistry = LocateRegistry.getRegistry("127.0.0.1", 1099);
13             
14            // search for myMessage service
15            Message impl = (Message) myRegistry.lookup("myMessage");
16             
17            // call server's method        
18            impl.sayHello("edwin");
19             
20            System.out.println("Message Sent");
21        } catch (Exception e) {
22            e.printStackTrace();
23        }       
24    }
25     
26    public static void main(String[] args) {
27        Main main = new Main();
28        main.doTest();
29    }
30}
This is the screenshot of what happen on my netbeans’ console, for my RMI client’s side
and RMI’s server side
and this is my netbeans’ project
While this is what RMI messages looks like
Have fun with RMI :)

http://www.infoq.com/cn/articles/cf-java-object-serialization-rmi

RMI

RMI(Remote Method Invocation)是Java中的远程过程调用(Remote Procedure Call,RPC)实现,是一种分布式Java应用的实现方式。它的目的在于对开发人员屏蔽横跨不同JVM和网络连接等细节,使得分布在不同JVM上的对象像是存在于一个统一的JVM中一样,可以很方便的互相通讯。之所以在介绍对象序列化之后来介绍RMI,主要是因为对象序列化机制使得RMI非常简单。调用一个远程服务器上的方法并不是一件困难的事情。开发人员可以基于Apache MINA或是Netty这样的框架来写自己的网络服务器,亦或是可以采用REST架构风格来编写HTTP服务。但这些解决方案中,不可回避的一个部分就是数据的编排和解排(marshal/unmarshal)。需要在Java对象和传输格式之间进行互相转换,而且这一部分逻辑是开发人员无法回避的。RMI的优势在于依靠Java序列化机制,对开发人员屏蔽了数据编排和解排的细节,要做的事情非常少。JDK 5之后,RMI通过动态代理机制去掉了早期版本中需要通过工具进行代码生成的繁琐方式,使用起来更加简单。
RMI采用的是典型的客户端-服务器端架构。首先需要定义的是服务器端的远程接口,这一步是设计好服务器端需要提供什么样的服务。对远程接口的要求很简单,只需要继承自RMI中的Remote接口即可。Remote和Serializable一样,也是标记接口。远程接口中的方法需要抛出RemoteException。定义好远程接口之后,实现该接口即可。如下面的Calculator是一个简单的远程接口。
public interface Calculator extends Remote {
    String calculate(String expr) throws RemoteException;
}  
实现了远程接口的类的实例称为远程对象。创建出远程对象之后,需要把它注册到一个注册表之中。这是为了客户端能够找到该远程对象并调用。
public class CalculatorServer implements Calculator {
    public String calculate(String expr) throws RemoteException {
        return expr;
    }
    public void start() throws RemoteException, AlreadyBoundException {
        Calculator stub = (Calculator) UnicastRemoteObject.exportObject(this, 0);
        Registry registry = LocateRegistry.getRegistry();
        registry.rebind("Calculator", stub);
    }
}
CalculatorServer是远程对象的Java类。在它的start方法中通过UnicastRemoteObjectexportObject把当前对象暴露出来,使得它可以接收来自客户端的调用请求。再通过Registryrebind方法进行注册,使得客户端可以查找到。
客户端的实现就是首先从注册表中查找到远程接口的实现对象,再调用相应的方法即可。实际的调用虽然是在服务器端完成的,但是在客户端看来,这个接口中的方法就好像是在当前JVM中一样。这就是RMI的强大之处。
public class CalculatorClient {
    public void calculate(String expr) {
        try {
            Registry registry = LocateRegistry.getRegistry("localhost");
            Calculator calculator = (Calculator) registry.lookup("Calculator");
            String result = calculator.calculate(expr);
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
} 
在运行的时候,需要首先通过rmiregistry命令来启动RMI中用到的注册表服务器。
为了通过Java的序列化机制来进行传输,远程接口中的方法的参数和返回值,要么是Java的基本类型,要么是远程对象,要么是实现了 Serializable接口的Java类。当客户端通过RMI注册表找到一个远程接口的时候,所得到的其实是远程接口的一个动态代理对象。当客户端调用其中的方法的时候,方法的参数对象会在序列化之后,传输到服务器端。服务器端接收到之后,进行反序列化得到参数对象。并使用这些参数对象,在服务器端调用实际的方法。调用的返回值Java对象经过序列化之后,再发送回客户端。客户端再经过反序列化之后得到Java对象,返回给调用者。这中间的序列化过程对于使用者来说是透明的,由动态代理对象自动完成。除了序列化之外,RMI还使用了动态类加载技术。当需要进行反序列化的时候,如果该对象的类定义在当前JVM中没有找到,RMI会尝试从远端下载所需的类文件定义。可以在RMI程序启动的时候,通过JVM参数java.rmi.server.codebase来指定动态下载Java类文件的URL。  

No comments:

Post a Comment