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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容