rmi-iiop


title: rmi-iiop
date: 2019-3-9
tags: [中间件,远程方法调用]
categories: 中间件结构


rmi

rmi结构

一、RMI

rmi结构

rmi即远程方法调用,字面意思就是可以在客户机上远程调用服务器上的方法。

3d7877359da9ce21cb5f57306d02f207.jpg

rmi可以分成三部分:第一层是存根(Stubs)和骨架(Skeletons),第二层是引用层,用来通过名字寻找对象,第三层是传输层,用于依赖于TCP/IP的客户机和服务机的通讯。

rmi流程

  1. 客户端调用远程方法,首先 Stubs 负责将调用的远程方法名和传入参数打包。
  2. 客户端通过引用层找到目标服务器,并通过传输层将数据传送到服务器。
  3. 服务器接受到数据,通过引用层,Skeletons解析远程方法名和传入参数。
  4. 服务器调用远程方法。
  5. 服务器将返回值或异常值沿相反方向返回。
  6. 客户端的 Stubs 解析返回数据。
238310f75b550014ddfdb1692b65c33a.png

实现的RMI的步骤

1.定义远程接口

远程对象需要由实现远程接口实现。远程接口继承于java.rmi.Remote,然后在接口内声明远程函数,要注意远程函数需要抛出java.rmi.RemoteException异常,用于调用远程函数失败时使用。

package example.hello;

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

public interface Hello extends Remote {
    String sayHello() throws RemoteException;
}

2.实现服务器

在服务器的主函数中。主要完成了两个任务。①实现远程对象的接口并导出为存根;②将远程对象的存根注册到java rmi中。

package example.hello;
        
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
        
public class Server implements Hello {
        
    public void Server() {}

    public String sayHello() {
        return "Hello, world!";
    }
        
    public static void main(String args[]) {
        
        try {
            Server obj = new Server();
            Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
            // Bind the remote object's stub in the registry
            Registry registry = LocateRegistry.getRegistry();
            registry.bind("Hello", stub);

            System.err.println("Server ready");
        } catch (Exception e) {
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        }
    }
}

在本例中,将服务器自身实现为远程对象。这样就在main函数中直接调用;将远程对象和服务器分开实现可以。

UnicastRemoteObject.exportObject这个静态方法导出了所需的远程对象,用以接收来自未知TCP端口的远程方法调用,并返回了一个用于传输远程对象给客户端的存根(stub);在执行完这个函数之后,java rmi 运行时将会在一个新的socket或者一个共享的socket中监听远程函数调用;stub中保存了主机名、端口以便联络远程对象。

对于客户端来说,要调用远程对象的方法,就必须知道远程对象的stub,所以服务器中将一个name和stub通过 registry.bind函数绑定起来。

本例中远程对象在实现函数sayHello()时并不需要抛出异常,因为这个函数本身的实现时不抛出任何异常的。

3.实现客户端

准备工作都做好了,使用起来就方便多了;在客户端中只需要在registry中通过name找到stub,然后调用远程方法就可以了。

package example.hello;

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

public class Client {

    private void Client() {}

    public static void main(String[] args) {

        String host = (args.length < 1) ? null : args[0];
        try {
            Registry registry = LocateRegistry.getRegistry(host);
            Hello stub = (Hello) registry.lookup("Hello");
            String response = stub.sayHello();
            System.out.println("response: " + response);
        } catch (Exception e) {
            System.err.println("Client exception: " + e.toString());
            e.printStackTrace();
        }
    }
}

LocateRegistry.getRegistry(host)函数表示指定主机名的registry,host为空表示使用本地的主机地址。

4.运行

Server exception: java.rmi.ConnectException: Connection refused to host: 192.168.44.1; nested exception is:
java.net.ConnectException: Connection refused: connect
java.rmi.ConnectException: Connection refused to host: 192.168.44.1; nested exception is:
java.net.ConnectException: Connection refused: connect
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
at sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
···

解决方法:确保在server启动之前,通过start rmiregistry 启动rmiregistry

Server exception: java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: com.whut.distributedTelephoneDirectory.Hello
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: com.whut.distributedTelephoneDirectory.Hello

···

解决方法:异常说明没有找到Hello类,确保rmiregistry已经在工程的bin(编译输出文件夹)目录下启动。

二 iiop

Internet Inter-ORB Protocol (IIOP)互联网内部对象请求代理协议,RMI-IIOP 将CORBA的功能添加到java-rmi中,为很多其他语言平台提供了互操作性和连沟通性; RMI-IIOP使java应用可以透明的调用远程网络服务提供的操作,并且这个操作使用了iiop工业标准。

RMI-IIOP可以让使用rmi编程的java程序员通过iiop来传输,RMI-IIOP为使用其他语言实现CORBA对象提供了互操作性,并且所有远程接口都可以只使用rmi进行定义。

实现的RMI的步骤

1.定义远程类的接口

//HelloInterface.java
import java.rmi.Remote;

public interface HelloInterface extends java.rmi.Remote {
   public void sayHello( String from ) throws java.rmi.RemoteException;
}

2. 定义实现类

//HelloImpl.java
import javax.rmi.PortableRemoteObject;

public class HelloImpl extends PortableRemoteObject implements HelloInterface {
   public HelloImpl() throws java.rmi.RemoteException {
       super();     // invoke rmi linking and remote object initialization
   }

   public void sayHello( String from ) throws java.rmi.RemoteException {
       System.out.println( "Hello from " + from + "!!" );
       System.out.flush();
   }
}

远程类实现接口并继承于PortableRemoteObject,可以让远程来使用基于IIOP传输的协议。

要注意构造函数要调用父类的构造函数PortableRemoteObject来导出远程对象,同时构造函数要抛出RemoteException异常,因为如果网络不通畅可能导致无法导出远程对象而产生异常。

3.定义服务器

//HelloServer.java
import javax.naming.InitialContext;
import javax.naming.Context;


public class HelloServer {
    public static void main(String[] args) {
        try {
            // Step 1: Instantiate the Hello servant
            HelloImpl helloRef = new HelloImpl();

            // Step 2: Publish the reference in the Naming Service 
            // using JNDI API
            
            //          Properties props = new Properties();
//            props.setProperty("java.naming.factory.initial","com.sun.jndi.cosnaming.CNCtxFactory");
//            props.setProperty("java.naming.provider.url", "iiop://localhost:2809");
//              
//            Context initialNamingContext = new InitialContext(props);
            
            Context initialNamingContext = new InitialContext();
            initialNamingContext.rebind("HelloService", helloRef );

            System.out.println("Hello Server: Ready...");

         } catch (Exception e) {
            System.out.println("Trouble: " + e);
            e.printStackTrace();
         } 
     }
}

4.定义客户端

//HelloClient.java
import java.rmi.RemoteException;
import java.net.MalformedURLException;
import java.rmi.NotBoundException;
import javax.rmi.*;
import java.util.Vector;
import javax.naming.NamingException;
import javax.naming.InitialContext;
import javax.naming.Context;

public class HelloClient {

    public static void  main( String args[] ) {
        Context ic;
        Object objref;
        HelloInterface hi;

        try {
            //Properties props = new Properties();
            //props.setProperty("java.naming.factory.initial","com.sun.jndi.cosnaming.CNCtxFactory");
            //props.setProperty("java.naming.provider.url", "iiop://localhost:2809");
            //ic = new InitialContext(props);
            ic = new InitialContext();
         
        // STEP 1: Get the Object reference from the Name Service
        // using JNDI call.
            objref = ic.lookup("HelloService");
            System.out.println("Client: Obtained a ref. to Hello server.");

        // STEP 2: Narrow the object reference to the concrete type and
        // invoke the method.
            hi = (HelloInterface) PortableRemoteObject.narrow(
                objref, HelloInterface.class);
            hi.sayHello( " MARS " );

        } catch( Exception e ) {
            System.err.println( "Exception " + e + "Caught" );
            e.printStackTrace( );
            return;
        }
    }
}

5.编译运行

首先将所有代码编译,然后要在生成java文件的路径bin下使用rmic 来生成skeletonsstubs

rmic -iiop HelloImpl

然后继续在该路径在开启Naming Service。

  start orbd -ORBInitialPort 2809

然后就可以依次启动服务器和客户端了。

reference:

http://blog.jobbole.com/104655/

http://zhuzhucs.com/2017/05/30/RMI-Helloworld/

http://zhuanlan.51cto.com/art/201806/576009.htm

http://jm.taobao.org/2018/06/13/%E5%BA%94%E7%94%A8/

https://docs.oracle.com/javase/6/docs/technotes/guides/rmi/hello/hello-world.html

你可能感兴趣的:(rmi-iiop)