深究Java中的RMI底层原理

本文介绍了Java中的RMI技术,通过一个简单的服务端和客户端示例,阐述了RMI的基本使用。接着探讨了RMI在分布式系统中的作用,包括Stub和Skeleton的角色以及Registry的作用。文章详细解析了RMI的底层工作原理,涉及服务端启动、客户端获取服务、远程服务调用等步骤,揭示了RMI如何通过Stub和Skeleton隐藏网络连接细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:随着一个系统被用户认可,业务量、请求量不断上升,那么单机系统必然就无法满足了,于是系统就慢慢走向分布式了,随之而来的是系统之间“沟通”的障碍。一般来说,解决系统之间的通信可以有两种方式:即远程调用和消息。RMI(Remote Method Invocation)就是远程调用的一种方式,也是这篇文章主要介绍的。


一、RMI的一个简单示例

这个示例拆分为服务端和客户端,放在两个idea项目中,并且通过了单机和双机两种环境的测试,是真正意义上的分布式应用。

项目结构

服务端应用: Server

主程序:        com.jnu.wwt.entry.Server

服务接口:     com.jnu.wwt.service.IOperation

服务实现:     com.jnu.wwt.service.impl.OperationImpl

客户端应用: Client

主程序:        com.jnu.wwt.entry.Client

服务接口:    com.jnu.wwt.service.IOperation


源码:

Server.java

/**
 * Created by wwt on 2016/9/14.
 */
public class Server {

    public static void main(String args[]) throws Exception{

        //以1099作为LocateRegistry接收客户端请求的端口,并注册服务的映射关系
        Registry registry=LocateRegistry.createRegistry(1099);

        IOperation iOperation=new OperationImpl();
        Naming.rebind("rmi://127.0.0.1:1099/Operation",iOperation);

        System.out.println("service running...");
    }

}

IOperation.java(服务端和客户端各需要一份)

/**
 * 服务端接口必须实现java.rmi.Remote
 * Created by wwt on 2016/9/14.
 */
public interface IOperation extends Remote{

    /**
     * 远程接口上的方法必须抛出RemoteException,因为网络通信是不稳定的,不能吃掉异常
     * @param a
     * @param b
     * @return
     */
    int add(int a, int b) throws RemoteException;

}

OperationImpl.java

/**
 * Created by wwt on 2016/9/14.
 */
public class OperationImpl extends UnicastRemoteObject implements IOperation{

    public OperationImpl() throws RemoteException {
        super();
    }

    @Override
    public int add(int a, int b) throws RemoteException{
        return a+b;
    }

}

Client.java

/**
 * Created by wwt on 2016/9/15.
 */
public class Client {

    public static void main(String args[]) throws Exception{
        IOperation iOperation= (IOperation) Naming.lookup("rmi://127.0.0.1:1099/Operation");
        System.out.println(iOperation.add(1,1));
    }

}

运行结果

先运行Server应用,服务就起来了。然后切换到Client应用,点击运行,Client调用Server的服务,返回结果。





二、RMI做了些什么

现在我们先忘记Java中有RMI这种东西。假设我们需要自己实现上面例子中的效果,怎么办呢?可以想到的步骤是:

  • 编写服务端服务,并将其通过某个服务机的端口暴露出去供客户端调用。
  • 编写客户端程序,客户端通过指定服务所在的主机和端口号、将请求封装并序列化,最终通过网络协议发送到服务端。
  • 服务端解析和反序列化请求,调用服务端上的服务,将结果序列化并返回给客户端。
  • 客户端接收并反序列化服务端返回的结果,反馈给用户。

这是大致的流程,我们不难想到,RMI其实也是帮我们封装了一些细节而通用的部分,比如序列化和反序列化,连接的建立和释放等,下面是RMI的具体流程:


这里涉及到几个新概念:

Stub和Skeleton:这两个的身份是一致的,都是作为代理的存在。客户端的称作Stub,服务端的称作Skeleton。要做到对程序员屏蔽远程方法调用的细节,这两个代理是必不可少的,包括网络连接等细节。

Registry:顾名思义,可以认为Registry是一个“注册所”,提供了服务名到服务的映射。如果没有它,意味着客户端需要记住每个服务所在的端口号,这种设计显然是不优雅的。


三、走进RMI原理之前,先来看看用到的类及其层次结构和主要的方法。





哪里看不懂随时回来看看结构。。。开始了



四、一步步解剖RMI的底层原理


  • 服务端启动Registry服务
Registry registry=LocateRegistry.createRegistry(1099);
从上面这句代码入手,追溯下去,可以发现服务端创建了一个RegistryImpl对象,这里做了一个判断。如果服务端指定的端口号是1099并且系统开启了安全管理器,那么可以在限定的权限集内(listen和accept)绕过系统的安全校验。反之则必须进行安全校验。这里纯粹是为了效率起见。真正做的事情在setUp()方法中,继续看下去。
public RegistryImpl(final int var1) throws RemoteException {
    if(var1 == 1099 && System.getSecurityManager() != null) {
        try {
            AccessController.doPrivileged(new PrivilegedExceptionAction() {
                public Void run() throws RemoteException {
                    LiveRef var1x = new LiveRef(RegistryImpl.id, var1);
                    RegistryImpl.this.setup(new UnicastServerRef(var1x));
                    return null;
                }
            }, (AccessControlContext)null, new Permission[]{
   new SocketPermission("localhost:" + var1
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值