乐趣区

关于java:Java-中-RMI-的使用

RMI 介绍

RMI (Remote Method Invocation) 模型是一种分布式对象利用,应用 RMI 技术能够使一个 JVM 中的对象,调用另一个 JVM 中的对象办法并获取调用后果。这里的另一个 JVM 能够在同一台计算机也能够是近程计算机。因而,RMI 意味着须要一个 Server 端和一个 Client 端。

Server 端通常会创立一个对象,并使之能够被近程拜访。

  • 这个对象被称为近程对象。
  • Server 端须要注册这个对象能够被 Client 近程拜访。

Client 端调用能够被近程拜访的对象上的办法,Client 端就能够和 Server 端进行通信并互相传递信息。

说到这里,是不是发现应用 RMI 在构建一个分布式应用时非常不便,它和 RPC 一样能够实现分布式应用之间的相互通信,甚至和当初的微服务思维都非常相似。

RMI 工作原理

正所谓“知其然知其所以然”,在开始编写 RMI 代码之前,有必要理解一下 RMI 的工作原理,RMI 中 Client 端是和 Server 端是如何通信的呢?

下图的能够帮忙咱们了解 RMI 的工作流程。

从图中能够看到,Client 端有一个被称 Stub 的货色,有时也会被成为存根,它是 RMI Client 的代理对象,Stub 的次要性能是申请近程办法时结构一个信息块,RMI 协定会把这个信息块发送给 Server 端。

这个信息块由几个局部组成:

  • 近程对象标识符。
  • 调用的办法形容。
  • 编组后的参数值(RMI 协定中应用的是对象序列化)。

既然 Client 端有一个 Stub 能够结构信息块发送给 Server 端,那么 Server 端必定会有一个接管这个信息快的对象,称为 Skeleton

它次要的工作是:

  • 解析信息快中的调用对象标识符和办法形容,在 Server 端调用具体的对象办法。
  • 获得调用的返回值或者异样值。
  • 把返回值进行编组,返回给客户端 Stub.

到这里,一次从 Client 端对 Server 端的调用后果就能够获取到了。

RMI 开发

通过下面的介绍,晓得了 RMI 的概念以及 RMI 的工作原理,上面介绍 RMI 的开发流程。

这里会通过一个场景进行演示,假如 Client 端须要查问用户信息,而用户信息存在于 Server 端,所以在 Server 端凋谢了 RMI 协定接口供客户端调用查问。

RMI Server

Server 端次要是构建一个能够被传输的类 User,一个能够被近程拜访的类 UserService,同时这个对象要注册到 RMI 凋谢给客户端应用。

  1. 定义服务器接口(须要继承 Remote 类,办法须要抛出 RemoteException)。

    package com.wdbyte.rmi.server;
    
    import java.rmi.Remote;
    import java.rmi.RemoteException;
    
    
    /**
     * RMI Server
     *
     * @author www.wdbyte.com
     * @date 2021/05/08
     */
    public interface UserService extends Remote {
    
        /**
         * 查找用户
         * 
         * @param userId
         * @return
         * @throws RemoteException
         */
        User findUser(String userId) throws RemoteException;
    }

User 对象在步骤 3 中定义。

  1. 实现服务器接口(须要继承 UnicastRemoteObject 类,实现定义的接口)。

    package com.wdbyte.rmi.server;
    
    import java.rmi.RemoteException;
    import java.rmi.server.UnicastRemoteObject;
    
    /**
     * @author www.wdbyte.com
     * @date 2021/05/08
     */
    public class UserServiceImpl extends UnicastRemoteObject implements UserService {protected UserServiceImpl() throws RemoteException { }
    
        @Override
        public User findUser(String userId) throws RemoteException {
            // 加载在查问
             if ("00001".equals(userId)) {User user = new User();
                user.setName("金庸");
                user.setAge(100);
                user.setSkill("写作");
                return user;
            }
            throw new RemoteException("查无此人");
        }
    }
  2. 定义传输的对象,传输的对象须要实现序列化(Serializable)接口。

    须要传输的类肯定要实现序列化接口,不然传输时会报错。IDEA 中如何生成 serialVersionUID,在文章开端也附上了简略教程。

    package com.wdbyte.rmi.server;
    
    import java.io.Serializable;
    
    /**
     *
     * @author www.wdbyte.com
     * @date 2021/05/08
     */
    public class User implements Serializable {
    
        private static final long serialVersionUID = 6490921832856589236L;
    
        private String name;
        private Integer age;
        private String skill;
    
        public String getName() {return name;}
    
        public void setName(String name) {this.name = name;}
    
        public Integer getAge() {return age;}
    
        public void setAge(Integer age) {this.age = age;}
    
        public String getSkill() {return skill;}
    
        public void setSkill(String skill) {this.skill = skill;}
        
        @Override
        public String toString() {
            return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", skill='" + skill + '\'' +
                '}';
        }
    }
  3. 注册(rmiregistry)近程对象,并启动服务端程序。

    服务端绑定了 UserService 对象作为近程拜访的对象,启动时端口设置为 1900。

    package com.wdbyte.rmi.server;
    
    import java.rmi.Naming;
    import java.rmi.registry.LocateRegistry;
    
    /**
     * RMI Server 端
     *
     * @author https://www.wdbyte.com
     * @date 2021/05/08
     */
    public class RmiServer {public static void main(String[] args) {
            try {UserService userService = new UserServiceImpl();
                LocateRegistry.createRegistry(1900);
                Naming.rebind("rmi://localhost:1900/user", userService);
                System.out.println("start server,port is 1900");
            } catch (Exception e) {e.printStackTrace();
            }
        }
    }

RMI Client

相比 Server 端,Client 端就简略的多。间接引入可近程拜访和须要传输的类,通过端口和 Server 端绑定的地址,就能够发动一次调用。

package com.wdbyte.rmi.client;

import java.rmi.Naming;

import com.wdbyte.rmi.server.User;
import com.wdbyte.rmi.server.UserService;

/**
 * @author https://www.wdbyte.com
 * @date 2021/05/08
 */
public class RmiClient {public static void main(String args[]) {
        User answer;
        String userId = "00001";
        try {
            // lookup method to find reference of remote object
            UserService access = (UserService)Naming.lookup("rmi://localhost:1900/user");
            answer = access.findUser(userId);
            System.out.println("query:" + userId);
            System.out.println("result:" + answer);
        } catch (Exception ae) {System.out.println(ae);
        }
    }
}

RMI 测试

启动 Server 端。

start server,port is 1900

启动 Client 端。

query:00001
result:User{name='金庸', age=100, skill='写作'}

如果 Client 端传入不存在的 userId。

java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
    java.rmi.RemoteException: 查无此人 

serialVersionUID 的生成

IDEA 中生成 serialVersionUID,关上设置,如下图所示勾选。

选中要生成 serialVersionUID 的类,按智能提醒快捷键。

参考

[1] https://docs.oracle.com/javas…

订阅

能够关注未读代码博客或者微信搜寻「 未读代码 」。

文章会在博客和公众号同步更新。

退出移动版