【修真院java小课堂】RMI简介

大家好,我是成都12期学员晋良金,今天为大家讲一下RMI。

1、背景介绍

RMI是Remote Method Invoke的缩写,是JDK提供的一个完善的、简单易用的远程调用框架,它要求客户端和服务器端都是Java程序。

2、知识剖析

如何去使用RMI呢?首先Remote接口用于标示其方法可以从非本地虚拟机上调用的接口。任何远程对象都必须直接或者间接实现此接口。只有在"远程接口"(扩展java.rmi.Remote的接口)中指定的这些方法才可以远程使用。也就是说要远程调用的方法必须在扩展remote接口的接口中声明,并且要抛出RemoteException异常才能被远程调用。远程对象必须实现java.server.UniCastRemoteObject。这样才能保证客户端访问获得远程对象的时候,该远程对象将会把自身的一个拷贝序列化后以socket的形式发送给客户端,此时客户端就会获得这个拷贝作为存根stub,而服务器本身已存在的远程对象称之为骨架。


3、常见问题

4、解决方案

5、编码实战

(1)服务端端接口继承Remote接口,告诉虚拟机此接口可以远程调用,方法抛出RemoteException异常。

public interface MyService extends Remote {
    //方法必须跑抽RemoteException
    String sendMessage(String message) throws RemoteException;
}

(2)服务端实现类继承UnicastRemoteObject 接口,并且需要在构造方法中抛出RemoteException 异常

public class MyServiceImpl extends UnicastRemoteObject implements MyService {
    //构造方法必须抛出RemoteException
    public MyServiceImpl() throws RemoteException {
        super();
    }

    public String sendMessage(String message) {
        System.out.println("调用服务端的sendMessage()");
        return message + "是的!";
    }
}

(3)启动服务端。需要将对象注册到注册表之中以供远程调用。

public class Service {
    public static void main(String[] args) {
        try {
            //注册端口号
            LocateRegistry.createRegistry(1099);
            MyService myService = new MyServiceImpl();
            Context context = new InitialContext();
            //注册对象,将对象与服务名绑定
            //bind():如果服务名已与其他对象绑定则抛出NameAlreadyBoundException异常
            //rebind():存在则新建
            context.rebind("rmi://localhost:1099/MyService", myService);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("创建服务端成功。");
    }
}

(4)客户端调用。需要一个跟服务端相同的接口,查找同名对象,即可调用远程方法。

public class Client {
    public static void main(String[] args) {
        try {
            Context context = new InitialContext();
            //lookup():查找与指定名称相同的对象
            MyService myService = (MyService) context.lookup("rmi://localhost:1099/MyService");
            System.out.println(myService.sendMessage("调用服务端成功了吗?"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6、扩展思考

7、参考文献

8、更多讨论

(1)如何实现对两个service的随机调用?

使用一个随机数对2求余实现随机。规定奇数调用service1、偶数调用service2。

(2)在一个service挂掉之后调用另一个service,如何实现?

写一个工具类,将两个service都实例了,使用try catch实现。

(3)RMI是如何实现的?


方法调用从客户对象经占位程序(Stub)、远程引用层(Remote Reference Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传 输层,向上穿过远程调用层和骨干网(Skeleton),到达服务器对象。 占位程序扮演着远程服务器对象的代理的角色,使该对象可被客户激活。 远程引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。传输层管理实际的连接,并且追踪可以接受方法调用的远程对象。服务器端的骨干网完成对服务器对象实际的方法调用,并获取返回值。返回值向下经远程引用层、服务器端的传输层传递回客户端,再向上经传输层和远程调用层返回。最后,占位程序获得返回值。

可以说,RMI由3个部分构成,第一个是RMIService即JDK提供的一个可以独立运行的程序(bin目录下的rmiregistry),第二个是RMIServer即我们自己编写的一个java项目,这个项目对外提供服务。第三个是RMIClient即我们自己编写的另外一个java项目,这个项目远程使用RMIServer提供的服务。
首先,RMIService必须先启动并开始监听对应的端口。
其次,RMIServer将自己提供的服务的实现类注册到RMIService上,并指定一个访问的路径(或者说名称)供RMIClient使用。
最后,RMIClient使用事先知道(或和RMIServer约定好)的路径(或名称)到RMIService上去寻找这个服务,并使用这个服务在本地的接口调用服务的具体方法。
通俗的讲完了再稍微技术的讲下:
首先,在一个JVM中启动rmiregistry服务,启动时可以指定服务监听的端口,也可以使用默认的端口。
其次,RMIServer在本地先实例化一个提供服务的实现类,然后通过RMI提供的Naming,Context,Registry等类的bind或rebind方法将刚才实例化好的实现类注册到RMIService上并对外暴露一个名称。
最后,RMIClient通过本地的接口和一个已知的名称(即RMIServer暴露出的名称)再使用RMI提供的Naming,Context,Registry等类的lookup方法从RMIService那拿到实现类。这样虽然本地没有这个类的实现类,但所有的方法都在接口里了,想怎么调就怎么调吧。
值得注意的是理论上讲RMIService,RMIServer,RMIClient可以部署到3个不同的JVM中,这个时候的执行的顺序是RMIService---RMIServer—RMIClient。另外也可以由RMIServer来启动RMIService这时候执行的顺序是RMIServer—RMIService—RMIClient。

猜你喜欢

转载自blog.csdn.net/qq_41810013/article/details/80872463
今日推荐