zoukankan      html  css  js  c++  java
  • rmi 工作原理

    RMI(Remote Method Invocation,远程方法调用)是Java的一组拥护开发分布式应用程序的API。RMI使用Java语言接口定义了远程对象,它集合了Java序 列化和Java远程方法协议(Java Remote Method Protocol)。它大大增强了Java开发分布式应用的能力。简单地说,这样使原先的程序在同一操作系统的方法调用,变成了不同操作系统之间程序的方 法调用。这也就说明,RMI可以实现在不同的JVM之间执行方法的调用。由于J2EE是分布式程序平台,RMI机制实现程序组件在不同操作系统之间的通 信。

    下图是RMI的体系结构:



    通过RMI的体系结构,已经可以看出RMI的大致工作原理:

    服务器端提供服务,服务中要暴露可以调用的远程方法,以接口的形式表现,这样在客户端可以通过服务接口来调用远程方法,实现复杂的业务逻辑。在服 务器端,首先要对接口中提供的方法实现,以便客户端调用能够完成一定的业务逻辑;接着需要生成Skeleton,在Skeleton中真正地实现了对商业 方法的调用,完成了客户请求的调用的过程,将获取到的调用方法的结果通过序列化机制返回给客户端,进行应答。在客户端,通过Stub来接收服务器返回的数 据(对象),即在这里进行了反序列化,也就是读取网络传输的字节流,进而进行重构。在Skeleton和Stub中,都对网络通信进行了处理,例如建立套 接字,建立网络连接,为实际的业务需要做好准备。

    可见,客户端并没有真正地执行与服务器端组件进行直接交互。

    这里,有个重要的概念,就是Java对象序列化机制。序列化,就是将对象写入流,以便能够在网络上传输对象,它是输出端执行的。反序列化,也就是 在接收端,为了能够获取传输的对象,需要将对象传输而来的字节流进行重构,重新得到完整的该对象。Java对象序列化的过程,是对已有的类的实例进行序列 化,首先要存在一个具体的实例。

    下面通过在客户端调用服务器端的方法,实现一个例子,可以很直观地模拟RMI工作,从而进一步深化对RMI工作机制的理解。我使用Oracle JDeveloper 10g开发测试。

    开发过程

    1、在服务接口中,将客户端可以进行调用的方法暴露给客户端。

    ShirdrnService接口中列出了可以调用的方法,ShirdrnService接口如下所示:
    Java代码  收藏代码
    1. package org.shirdrn.rmi.server;  
    2.   
    3. import java.io.IOException;  
    4.   
    5. public interface ShirdrnService {  
    6.     public String getServerTime() throws IOException, ClassNotFoundException;  
    7.     public String getGreetings () throws IOException, ClassNotFoundException;  
    8. }  

    2、在服务器端要真正实现服务接口中的这些方法,ShirdrnServiceImpl类实现了ShirdrnService接口中列出的方法。

    ShirdrnServiceImpl类的实现如下所示:
    Java代码  收藏代码
    1. package org.shirdrn.rmi.server;  
    2.   
    3. import java.io.IOException;  
    4.   
    5. import java.util.Date;  
    6.   
    7. public class ShirdrnServiceImpl implements ShirdrnService {  
    8.     public ShirdrnServiceImpl() {  
    9.     }  
    10.   
    11.     public String getServerTime() throws IOException, ClassNotFoundException{  
    12.         Date date = new Date();  
    13.         String time = date.toLocaleString();  
    14.         return time;  
    15.     }  
    16.   
    17.     public String getGreetings() throws IOException, ClassNotFoundException{  
    18.         Date date = new Date();  
    19.         int hour = date.getHours();  
    20.         String greetings = "";  
    21.         if(hour < 12 && hour > 6){  
    22.             greetings = "上午好!";  
    23.         }  
    24.         else if(hour <= 18 && hour > 12){  
    25.                 greetings = "下午好!";  
    26.             }  
    27.             else{  
    28.                 greetings = "晚上好!";  
    29.             }  
    30.         return greetings;  
    31.     }  
    32. }  

    3、客户端Stub的实现

    ShirdrnStub类实现了ShirdrnService接口,它是客户端获取服务结果数据最直接的实现。然而,它并没有与服务器端真正实现服务接口的实现类直接打交道,而是通过网络传输获取到调用方法的执行结果数据。
    Java代码  收藏代码
    1. package org.shirdrn.rmi.client;  
    2.   
    3. import java.io.IOException;  
    4.   
    5. import java.io.ObjectInputStream;  
    6. import java.io.ObjectOutputStream;  
    7.   
    8. import java.net.Socket;  
    9.   
    10. import java.net.UnknownHostException;  
    11.   
    12. import org.shirdrn.rmi.server.ShirdrnService;  
    13.   
    14. public class ShirdrnStub implements ShirdrnService{  
    15.   
    16.     Socket socket;  
    17.   
    18.     public ShirdrnStub() {  
    19.         try {  
    20.             socket = new Socket("56987b31c0b246d",8888);  
    21.         } catch (UnknownHostException e) {  
    22.             e.printStackTrace();  
    23.             System.out.println("[信息]创建套接字异常:未知的主机名称。");  
    24.         } catch (IOException e) {  
    25.             e.printStackTrace();  
    26.         }  
    27.     }  
    28.   
    29.   
    30.     public String getServerTime() throws IOException, ClassNotFoundException {  
    31.         ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());  
    32.         oos.writeObject("getServerTime");  
    33.         oos.flush();  
    34.         ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());   
    35.         return (String)ois.readObject();  
    36.     }  
    37.   
    38.     public String getGreetings() throws IOException, ClassNotFoundException {  
    39.         ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());  
    40.         oos.writeObject("getGreetings");  
    41.         oos.flush();  
    42.         ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());   
    43.         return (String)ois.readObject();  
    44.     }  
    45. }  

    4、服务器端Skeleton的实现

    ShirdrnSkeleton类继承了Thread,因此它可以启动服务器端线程,而且是单线程。在 ShirdrnSkeleton中,真正与提供服务实现的类进行了交互。在建立起连接以后,首先获取到客户端调用的方法,根据客户的选择来直接与服务的实 现交互,执行方法获得执行结果数据,从而将结果数据进行序列化,通过网络传输给客户完成应答过程。

    实例化ShirdrnSkeleton类的一个实例后,启动线程,这很像是一个服务器端监听器,监听客户端动作,从而完成服务的请求。
    Java代码  收藏代码
    1. package org.shirdrn.rmi.server;  
    2.   
    3. import java.io.ObjectInputStream;  
    4.   
    5. import java.io.ObjectOutputStream;  
    6.   
    7. import java.net.ServerSocket;  
    8. import java.net.Socket;  
    9.   
    10. public class ShirdrnSkeleton extends Thread {  
    11.     ShirdrnServiceImpl shirdrnServiceImpl;  
    12.     public ShirdrnSkeleton(ShirdrnServiceImpl shirdrnServiceImpl) {  
    13.         this.shirdrnServiceImpl = shirdrnServiceImpl;  
    14.     }  
    15.     public void run() {            
    16.         try {  
    17.             ServerSocket serverSocket = new ServerSocket(8888);          
    18.             Socket socket = serverSocket.accept();  
    19.             while(socket != null){  
    20.                 ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());  
    21.                 String method = (String)ois.readObject();  
    22.                 if(method.equals("getServerTime")){  
    23.                     String serverTime = shirdrnServiceImpl.getServerTime();  
    24.                     ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());  
    25.                     oos.writeObject(serverTime);  
    26.                     oos.flush();  
    27.                 }  
    28.                 if(method.equals("getGreetings")){  
    29.                     String greetings = shirdrnServiceImpl.getGreetings();  
    30.                     ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());  
    31.                     oos.writeObject(greetings);  
    32.                     oos.flush();  
    33.                 }  
    34.             }  
    35.         } catch (Exception e) {  
    36.            e.printStackTrace();  
    37.         }  
    38.     }  
    39.       
    40.     public static void main(String[] args){  
    41.         ShirdrnServiceImpl ssi = new ShirdrnServiceImpl();  
    42.         ShirdrnSkeleton skeleton = new ShirdrnSkeleton(ssi);  
    43.         skeleton.start();  
    44.     }     
    45.       
    46. }  

    5、客户端测试程序

    通过ShirdrnClient类中的主函数,实现了对服务器端提供的服务接口中的方法进行调用:
    Java代码  收藏代码
    1. package org.shirdrn.rmi.client;  
    2.   
    3. import java.io.IOException;  
    4.   
    5. import org.shirdrn.rmi.server.ShirdrnService;  
    6.   
    7. public class ShirdrnClient {  
    8.     public ShirdrnClient() {  
    9.     }  
    10.   
    11.     public static void main(String[] args) throws IOException,   
    12.                                                   ClassNotFoundException {  
    13.         ShirdrnService stub = new ShirdrnStub();  
    14.         System.out.println("正在获取服务器时间...");  
    15.         System.out.println("服务器时间 : "+stub.getServerTime());  
    16.         System.out.println("正在获取问候语...");  
    17.         System.out.println("问候语为 : "+stub.getGreetings());          
    18.     }  
    19. }  

    6、测试过程


    心得总结

    1、客户端是直接通过实例化一个Stub,因为Stub实现了服务方暴露的接口,所以可以直接通过一个Stub的实例来调用服务方法。但是,这并没有直接调用服务方的服务接口的实现。

    2、服务方具有服务接口的真正实现。而真正调用服务接口实现类是在服务器端的Skeleton中,在Skeleton中负责调用真正的服务实现,将执行结果返回给客户端,屏蔽了方法调用的实现细节。

    3、客户端Stub和服务器端Skeleton之间建立起网络连接,进行了网络通信,主要就是对执行结果进行传输/接收,Skeleton将结果通过网络传输到客户端,而Stub通过代理接口接收传输而来的结果数据。
  • 相关阅读:
    SpringCloud Alibaba微服务实战十
    万字长文!分布式锁的实现全都在这里了
    python编程中的小技巧(持续更新)
    工作十年的数据分析师被炒,没有方向,你根本躲不过中年危机
    github入门操作快速上手
    167. 两数之和 II
    167. 两数之和 II
    167. 两数之和 II
    怎么使用Fiddler进行抓包
    怎么使用Fiddler进行抓包
  • 原文地址:https://www.cnblogs.com/fsh1542115262/p/4817441.html
Copyright © 2011-2022 走看看