zoukankan      html  css  js  c++  java
  • java RMI 远程调用

    1.背景

    在学习代理模式的过程中接触到了远程调用,jdk有自己的RMI实现,所以这边自己实现了RMI远程调用,并记录下心得.

    感受最深的是RMI和现在的微服务有点相似,都是通过"注册中心"来获取数据,比如spring cloud 中通过feign来获取数据,这个就可以看作一个代理模式,我们通过feigh获取数据其实是通过别的服务器上的代码来获取数据的,而RMI中是通过rmiRegistry注册中心来注册,并且通过 

    Naming.lookup("rmi://127.0.0.1/RemoteHello")

    这个方法来获取数据.      其实本质都差不多,spring cloud 数据传输格式主要是 json, 而 RMI主要是二进制,因为要传输的对象都实现了Serializable接口. 可能RMI在分布式这边用的多一些吧,但是我没有接触过,还不清楚.

    2.代码实现

    这边有点坑,因为一直用eclipse编译代码,都忘记了cmd命令行怎么编译了,结果被包名给搞糊涂了,这边记录总结下,希望有人碰到这个问题不要在纠结太久.

    我们先定义自己的远程接口MyRemote

    package state.remote;
    
    import java.rmi.Remote;
    import java.rmi.RemoteException;
    
    public interface MyRemote extends Remote {
        public String sayHello() throws RemoteException;
    }

    这边定义了一个方法,返回String,String已经实现了Serializable接口,所以我们可以直接使用.  请注意上面的包名: package state.remote

     

    定义实现类

    package state.remote;
    
    import java.net.MalformedURLException;
    import java.rmi.Naming;
    import java.rmi.RemoteException;
    import java.rmi.server.UnicastRemoteObject;
    
    
    public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
    
        /**
         * 
         */
        private static final long serialVersionUID = 927479549518191259L;
    
    
        protected MyRemoteImpl() throws RemoteException {
        }
    
    
    
        public static void main(String[] args) {
            try {
                MyRemote service = new MyRemoteImpl();
                //注册
                Naming.rebind("RemoteHello", service);
            } catch (RemoteException e) {
                e.printStackTrace();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
    
    
    
        @Override
        public String sayHello() throws RemoteException {
            return "Hello";
        }
    }

    通过Naming的rebind方法我们可以把RemoteHello注册到服务上.  还是注意报名:package state.remote

     

    定义客户端

    package state.remote;
    
    import java.net.MalformedURLException;
    import java.rmi.Naming;
    import java.rmi.NotBoundException;
    import java.rmi.RemoteException;
    
    public class MyRemoteClient {
        public static void main(String[] args) {
            new MyRemoteClient().go();
        }
        
        public void go() {
            try {
                MyRemote service = (MyRemote) Naming.lookup("rmi://127.0.0.1/RemoteHello");
                String s = service.sayHello();
                System.out.println(s);
            } catch (MalformedURLException | RemoteException | NotBoundException e) {
                e.printStackTrace();
            }
        }
    }

    客户端通过Naming的lookup方法可以获取数据.

     

    接下来我们就进入坑吧

    坑1.

    我们进入到目录

    打开windows特有的power shell 进行编译,power shell 我觉得最大的好处就是不用cd进入目录了

     

    ok,第一个坑来了

    MyRemoteImpl.java:24: 错误: 找不到符号

     

    MyRemoteImpl.java:36: 错误: 方法不会覆盖或实现超类型的方法

    这两个方法都是因为实现了接口,没有和接口一起编译所以报错,我们以前写的helloworld根本没有接口一说,只是 System.out.println("HelloWorld"),所以根本发现不了这个错误.

    解决:

    和接口一起编译

    出现.class文件了

    或者

    javac *.java

    也可以,直接把当前目录所有.java文件编译

     

    ok,我们有.class文件了,可以用jdk的rmi生成stub文件了吧,(注意:stub文件相当于本地的远程调用接口,属于rmi的概念,这边以后有机会在写)

    error: File .MyRemoteImpl.class does not contain type MyRemoteImpl as expected, but type state.remote.MyRemoteImpl. Please remove the file, or make sure it appears in the correct subdirectory of the class path.
    error: 找不到类MyRemoteImpl。

    又进入了第二个坑

    坑 2.

    还记得上面的package 包名吗,因为rmic 在编译.class文件的时候需要识别包名,所以我们只能 javac state.remote.MyRemoteImpl用来实现, 要和上面的package一致.

    我们来尝试下

     ok,坑3

    我们来到坑3了

    说找不到这个类,很简单,因为需要在这个目录下在创建文件夹,谁让 package 包名是这样写的

    在这个目录下创建,

    再在state目录下创建

    ,

    然后我们进入remote目录,把这两个文件方法到这里.,

    在执行rmic命令,

    ok没有报错,这里说骨架不在必要,因为整合到了server中,ok,多了MyRemoteImpl_Stub.class,我们的客户就是通过这个访问远程虚拟机上的数据.

    既然MyRemoteImpl_Stub.class在这个目录,我们就在这里注册服务吧

    rmiregistry生成一个注册中心,然后运行 javac MyRemoteImpl 执行之前写的 rebind方法

     好,只是一个远程调用这么多坑,第4个坑了,这边还是相同的错误,因为 package 之前写的是 state.remote,所以我们必须在这个目录下才能执行.class文件.

    我们回到之前最开始的地方

     注册中心

     执行 rebind代码

    可以了,rmiregistry必须和 java state.remoteMyRemoteImpl在同一个目录运行,因为rmiregistry会从classpath中的 .;  也就是当前目录寻找 _Stub结尾的文件,所以我们需要在同一个目录运行.或者把classpath设置要运行的rmiregisry目录也可以.

     

    准备工作做好了,注册中心启动了.也注册了方法"RemoteHello",我们执行客户端

    还是一样的错误....

    我们需要编译MyRemoteClient,并把生成好的.class文件放到 

    _Stub目录下

    执行

    历尽千辛万苦,终于得到我们想要的结果了.

    不容易.

    3.总结

    一句话:用eclipse编写代码真幸福.

    (最需要注意的就是package 这个问题,如果只是测试的话不需要保留package,或者直接默认路径就可以,如果要保留package的话一定要设置好目录,因为 javac  rmic 需要注意路径问题.)

  • 相关阅读:
    UIimageView和UIimage的小区别
    transform
    block的一些注意事项
    category与协议的不同
    类扩展和category的小区别
    category的概念
    OC中的类扩展
    Java学习 第三章 java基础(三)
    Java学习 第三章 java基础(二)
    Java学习 第三章 java基础(一)
  • 原文地址:https://www.cnblogs.com/lishuaiqi/p/11318228.html
Copyright © 2011-2022 走看看