当前位置: 首页 > 最新文章 > 正文

自己写了个Java RMI(远程方法调用)的实现案例

自己简单写了个Java RMI的实现案例。为了更好理解RMI、序列化的意义等等,花费三天多的时间肝了一个Java RMI的实现案例。!!!代码量有点大,先附上了简图用于理解整个过程分为两大步第一步--注册过程:客户端通过指定路由获取注册中心指定的远程客户端对象;第二部--服务调用过程:客户端通过远程客户端对象访问远程服务端从而访问到真实服务的实现调整为舒适的姿势,慢慢看……废话少说,上代码!!!自

admin

自己简单写了个Java RMI的实现案例。为了更好理解RMI、序列化的意义等等,花费三天多的时间肝了一个Java RMI的实现案例。!!!代码量有点大,先附上了简图用于理解整个过程分为两大步第一步--注册过程:客户端通过指定路由获取注册中心指定的远程客户端对象;第二部--服务调用过程:客户端通过远程客户端对象访问远程服务端从而访问到真实服务的实现调整为舒适的姿势,慢慢看……废话少说,上代码!!!

自己简单写了个Java RMI(远程方法调用)的实现案例。

为了更好理解RMI(远程方法调用)、序列化的意义等等,花费三天多的时间肝了一个Java RMI的实现案例。

!!!高能预警!!!

代码量有点大,先附上了简图用于理解

自己写了个Java RMI(远程方法调用)的实现案例

整个过程分为两大步

  • 第一步--注册过程:客户端通过指定路由获取注册中心指定的远程客户端对象
  • 第二部--服务调用过程:客户端通过远程客户端对象访问远程服务端(代理服务)从而访问到真实服务的实现

调整为舒适的姿势,慢慢看…… 废话少说,上代码!!!

1.定义远程标记接口

面向接口编程,具体作用看后面的代码怎么使用

// 标记接口:直接或间接实现MyRMI接口将获得远程调用的能力public interface MyRMI{}

2.编写RMI 服务注册中心

注册中心类:用于注册服务和获取服务,核心是hashMap路由表对象

/** * 注册中心:维护服务发布的注册表 */public class MyRMIRegistry {    // 默认端口    public final int REGISTRY_PORT = 10099;    private String host;    private int port;    private Map<String, MyRMI> bindings;    public MyRMIRegistry(int port){        this.port = port;    }    public MyRMIRegistry(String host, int port){        this.host=host;        this.port=port;    }    public void createRegistry(String serverName,MyRMI myRMI){        // 注册服务,并开启服务        this.bindings = new HashMap<>();        String host = null;        try {            host = Inet4Address.getLocalHost().getHostAddress();        } catch (UnknownHostException e) {            e.printStackTrace();        }        // 路由规则可自行定义,只要能确保Key唯一即可        String binding = "myrmi://"+host+":"+port+"/"+serverName;        this.bindings.put("myrmi://"+host+":"+port+"/"+serverName,myRMI);        System.out.println("注册的服务有:"+bindings.keySet().toString());        MyRMIRegistryServer myRMIRegistryServer = new MyRMIRegistryServer(this.port, this.bindings);        Executors.newCachedThreadPool().submit(myRMIRegistryServer); // 线程池启动服务    }    public MyRMI getRegistry(String serverName){        Socket socket = null;        ObjectOutputStream out = null;        ObjectInputStream in = null;        MyRMI myRMI = null;        // 通过        try {            socket = new Socket(host, port);            out = new ObjectOutputStream(socket.getOutputStream());            out.writeObject("myrmi://"+host+":"+port+"/"+serverName);            in = new ObjectInputStream(socket.getInputStream());            myRMI = (MyRMI)in.readObject();        } catch (IOException e) {            e.printStackTrace();        }catch (ClassNotFoundException e) {            e.printStackTrace();        }        return myRMI;    }}

RMI 注册中心获取服务的线程:启动注册中心服务,等待客户端来获取路由表中的远程客户端

/** * RMI注册中心获取服务线程 */public class MyRMIRegistryServer implements Runnable {    private int port;    private Map<String, MyRMI> bindings;    public MyRMIRegistryServer(Integer port,Map<String, MyRMI> bindings){        this.port = port;        this.bindings = bindings;    }    @Override    public void run() {        ServerSocket serverSocket = null;        ObjectOutputStream out = null;        ObjectInputStream in = null;        try {            serverSocket = new ServerSocket(this.port);            while(true){                Socket socket = serverSocket.accept();                in = new ObjectInputStream(socket.getInputStream());                out = new ObjectOutputStream(socket.getOutputStream());                // 看看客户端想要什么服务                String serverName = (String)in.readObject();                Iterator iterator = bindings.keySet().iterator();                while (iterator.hasNext()){                    String key = (String) iterator.next();                    if(serverName.equals(key)){                        // 给客户端响应服务对象                        MyRMI myRMI = bindings.get(key);                        out.writeObject(myRMI);                    }                }            }        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }finally {            // 异常后进入            try {                if (out!=null)  out.close();                if (in!=null)   in.close();                if (serverSocket!=null) serverSocket.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}

3.定义要发布的服务接口

需要提供RMI服务的接口,必须继承自定义的MyRMI标记接口

/** * 服务接口 */public interface Hello extends MyRMI {    public String sayHello(String name);}

4.服务用到的实体类

/** * 对象数据类:Person */public class Person implements Serializable {    // 序列化版本UID    private static final long serialVersionUID = 1L;    private String name;    private int age;    private String sex;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public String getSex() {        return sex;    }    public void setSex(String sex) {        this.sex = sex;    }    @Override    public String toString() {        return "Person{" + "name='" + name + ", age=" + age + ", sex='" + sex + '}';    }    public Person() {    }    public Person(String name, Integer age, String sex) {        this.name = name;        this.age = age;        this.sex = sex;    }}

5.实现要发布的服务接口

/** * 对外提供的服务实现 */public class HelloImpl implements Hello {    private static File file = new File("D:/HelloRMI.txt");    private static List<Person> list = new ArrayList<>();    @Override    public String sayHello(String name) {        String result = "没有获取到"+name+"的信息";        try {            List<Person> personList = readList();            for(Person person:personList){                if (person.getName().equals(name)){                    result = "Hello , welcome to the RMI! "                            + "姓名:"+name + " 年龄:"+person.getAge()+" 性别:"+person.getSex();                }            }        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        return result;    }    /**     * 生成数据,为测试做准备     * @param args     * @throws IOException     * @throws ClassNotFoundException     */    public static void main(String[] args) throws IOException, ClassNotFoundException {        //数据准备:集合类都实现了序列化接口Serializable        list.add(new Person("张三", 38, "男"));        list.add(new Person("李四", 38, "男"));        list.add(new Person("如花", 18, "女"));        // 持久化对象数据        writerList(list);        // 查询持久化对象数据        List<Person> personList = readList();        System.out.println("遍历持久化对象数据>");        for (Person person : personList) {            System.out.println(person);            if (person.getAge() == 38) {                person.setAge(18);            }        }    }    public static void writerList(List<Person> list) throws IOException {        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));        objectOutputStream.writeObject(list);        objectOutputStream.close();    }    public static List<Person> readList() throws IOException, ClassNotFoundException {        // 读取普通文件反序列化        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));        List<Person> personList = (List<Person>) objectInputStream.readObject();        objectInputStream.close();        return personList;    }}

6.远程客户端的线程类

用于自动生成服务接口(继承了MyRMI标记接口)的远程客户端类:这个类原本是通用类实现,为了方便实现,就直接实现Hello接口了

/** * 远程客户端的线程类的生成: *      为了方便实现,这边直接实现服务接口编写 */public class HelloClientThread implements Hello,Serializable {    // 序列化版本UID    private static final long serialVersionUID = 1L;    private Map<String,Object> map = new HashMap<>(); // 报文对象:方法名和参数对象    private String ip;    private int port;    public HelloClientThread(String ip, int port){        this.ip = ip;        this.port = port;    }    @Override    public String sayHello(String name) {        map.put("sayHello",name);        String result = (String)send();        return result;    }    private Object send(){        Object o =null;        Socket socket = null;        ObjectOutputStream out = null;        ObjectInputStream in = null;        try {            socket = new Socket(ip, port);            out = new ObjectOutputStream(socket.getOutputStream());            in = new ObjectInputStream(socket.getInputStream());            // 告诉服务端我要调用什么服务            out.writeObject(map);            // 获取服务实现对象            o = in.readObject();        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }finally {            try {                if (out!=null)  out.close();                if (in!=null)   in.close();                if (socket!=null) socket.close();            } catch (IOException e) {                e.printStackTrace();            }        }        return o;    }}

7.远程服务端的线程类

用于自动生成服务接口(继承了MyRMI标记接口)的远程服务端类:这个类原本也是通用类实现,为了方便实现,部分代码尚未做到解耦通用

/** * 远程服务端的线程类的生成: *      为了方便实现,这边直接实现服务线程类 */public class HelloServerThread implements Runnable {    private Integer port;    private MyRMI myRMI;    public HelloServerThread(Integer port, MyRMI myRMI){        this.port = port;        this.myRMI = myRMI;    }    @Override    public void run() {        ServerSocket serverSocket = null;        ObjectOutputStream out = null;        ObjectInputStream in = null;        try {            serverSocket = new ServerSocket(this.port);            while(true){                Socket socket = serverSocket.accept();                in = new ObjectInputStream(socket.getInputStream());                out = new ObjectOutputStream(socket.getOutputStream());                // 看看客户端想要什么服务                Map map = (Map)in.readObject();                Iterator iterator = map.keySet().iterator();                while (iterator.hasNext()){                    String key = (String) iterator.next();                    if("sayHello".equals(key)){                        // 给客户端响应服务对象                        Hello hello = (Hello)myRMI;                        String result = hello.sayHello((String) map.get(key));                        out.writeObject(result);                    }                }            }        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }finally {            // 异常后进入            try {                if (out!=null)  out.close();                if (in!=null)   in.close();                if (serverSocket!=null) serverSocket.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }    }

8.远程客户端生成和远程服务端生成和启动的类

/** * 远程客户端生成和远程服务端生成和启动的类 */public class RemoteSocketObject{    // 默认端口    private int port=18999;    // 指定远程通讯端口和代理服务    public MyRMI createRemoteClient(MyRMI myRMI,int port){        if (port > 0)            this.port=port;        MyRMI myRMIClient = null;        try {            // 生成底层通讯服务端,并启动            HelloServerThread helloServerThread = new HelloServerThread(this.port, myRMI);            Executors.newCachedThreadPool().submit(helloServerThread); // 线程池启动服务            // 生成底层通讯客户端            String localHost = Inet4Address.getLocalHost().getHostAddress();            System.out.println("host="+localHost+",port="+this.port);            myRMIClient= new HelloClientThread(localHost, this.port);        } catch (Exception e) {            e.printStackTrace();        }        return myRMIClient;    }    }

9.服务发布类

/** * RMI 服务发布类 */public class HelloServer {    public static void main(String[] args) {        System.out.println("Create Hello Remote Method Invocation...");        // 实例化一个Hello        Hello hello = new HelloImpl();        // 转换成远程服务,并提供远程客户端        Hello remoteClient = (Hello)new RemoteSocketObject().createRemoteClient(hello, 0);        // 将服务实现托管到Socket服务        MyRMIRegistry myRMIRegistry = new MyRMIRegistry(16000);        // 开启线程服务        myRMIRegistry.createRegistry("Hello",remoteClient);    }}

10.客户端测试类

/** * 客户端测试类 *      客户端只知道服务接口、服务发布的地址和服务发布的名称 */public class TestHello {    public static void main(String[] args) {        // 注意不是127.0.0.1,不知道host的看server端启动后打印的信息        // 端口16000是注册中心的端口,底层代理服务的端口客户端无需知道        MyRMIRegistry client = new MyRMIRegistry("192.168.233.1", 16000);        Hello hello = (Hello) client.getRegistry("Hello");        System.out.println(hello.sayHello("张三"));    }}

11.总结

所有代码整下来,在真正的场景中:

客户端只知道:TestHello类、Hello接口定义、MyRMI标记接口、MyRMIRegistry注册类代码(路由表中只知道Key,不知道具体值);

服务端只知道:Hello接口、HelloImpl服务实现类、MyRMI标记接口、MyRMIRegistry注册类代码(路由表中知道Key和具体值);

关于其他的代码实现都是无感的,为了简单实现远程客户端和远程服务端,将服务接口耦合到两者上了,未做到解耦通用。

来源:https://www.cnblogs.com/dennyLee2025/p/15969994.html


上一篇: 15家国内基因治疗赛道企业盘点 下一篇:怎么复制电脑软件安装包(安装包复制到电脑上怎么安装)
返回顶部