分布式通信:彻底剖析RMI底层源码 、手写轻量级RMI框架

版权声明:如需转载,请标明转载出处哦! https://blog.csdn.net/Z0157/article/details/82049975

一、开篇语

上一篇已经详细介绍了什么是RMI,RMI的作用,RMI框架的通信的过程是如何实现的。
现在我们带着疑问去解析RMI源码底层实现:
1、客户端和服务器端是如何创建的?
2、客户端和服务器端的信息是如何传输的?
3、RMI 有没有什么优点,有什么缺点?
源码阅读到什么地步?阅读到能解决自己的疑问,能放过自己的地步就足以。

二、源码解析

1、服务器端本地创建远程注册中心Registry,并把实例化的远程对象注册进去;

(1)远程对象的服务接口要继承Remote,而服务的具体实现类要继承UnicastRemoteObject;
(2)通过端口信息,在本地服务器创建或者导出一个注册服务实例。

LocateRegistry.createRegistry(port);
Naming.bind("rmi://localhost:"+port+"/sayHello",iSayHello);

我们先来了解LocateRegistry的作用:

通过源码可以看出它所提供的的接口的作用是:去获得/创建一个绑定指定服务器(包括本机)和特定端口的远程对象注册服务的的实例;

提供2种方式创建:

(1)、只提供port,默认方式创建

public static Registry createRegistry(int port) throws RemoteException {
    return new RegistryImpl(port);
}

(2)、通过指定的端口,和指定套接字创建工厂创建。(分析到这里我们会发现RMI的通信肯定是基于TCP的,和Socket脱不了干系)

public static Registry createRegistry(int port,
                                      RMIClientSocketFactory csf,
                                      RMIServerSocketFactory ssf)
    throws RemoteException
{
    return new RegistryImpl(port, csf, ssf);
}

 

我们走进 new RegistryImpl(port);

public RegistryImpl(final int var1) throws RemoteException {
对于跨主机的访问,RMI加入了安全管理器(SecurityManager),那么也需要对应的安全策略文件;
    if (var1 == 1099 && System.getSecurityManager() != null) {
        try {
            AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
                public Void run() throws RemoteException {
                    LiveRef var1x = new LiveRef(RegistryImpl.id, var1);
                    最终实现
                    RegistryImpl.this.setup(new UnicastServerRef(var1x, (var0) -> {
                        return RegistryImpl.registryFilter(var0);
                    }));
                    return null;
                }
            }, (AccessControlContext)null, new SocketPermission("localhost:" + var1, "listen,accept"));
        } catch (PrivilegedActionException var3) {
            throw (RemoteException)var3.getException();
        }
    } else {
       最终实现
        LiveRef var2 = new LiveRef(id, var1);
        this.setup(new UnicastServerRef(var2, RegistryImpl::registryFilter));
    }

}

 

最终实现在setup()里,点进去看;

扫描二维码关注公众号,回复: 3103753 查看本文章

LiveRef:是一个可clone的live对象引用,创建一个TCPEndpoint,这个为后面创建socket服务做准备;

new UnicastServerRef(var2, RegistryImpl::registryFilter)

构建一个远程服务引用对象参数;传入setup();其中registryFilter是过滤输入的对象是否支持java的序列化的作用;

private void setup(UnicastServerRef var1) throws RemoteException {
    this.ref = var1;
    var1.exportObject(this, (Object)null, true);
}

var5 = Util.createProxy(var4, this.getClientRef(), this.forceStubUse);

创建远程代理对象,getClientRef()实现了InvocaltionHandler,动态代理内部方法回调方式,提供了TCP通信创建;

 
 

最终注册服务创建完成,把服务相关信息封装在Target上,并且暴露他的TCP端口,提供给客户端远程访问;

同时执行代码

his.ref.exportObject(var6);

创建了客户端的远程访问的注册中心的代理对象RemoteSub对象实例;

return createStub(var3, var1);

到这里服务器的启动已经完成,但是我们别忘记了,服务器端的socketserver是如何启动的:

他就是我上面说的动态代理 截图中的步骤2,invoke()方法里,创tcpconnection

 

通过源码发现通信是BIO而不是NIO。是阻塞的;

后面就是把远程访问代理对象绑定到请求的url上,其实服务器用的就是一个hashTable map集合来维护的;

客户端创建;

ISayHello iSayHello = (ISayHello) Naming.lookup("rmi://localhost:" + port + "/sayHello");

从注册服务中心获取远程访问的对象sub,服务器端会对应生成一个RomteSkeleton代理对象,负责和client的remoteSutub对象进行tcp通信;

到这里我们大概就了解RMI框架底层实现原理了;

个人总结:

RMI出来的非常的早,对现在分布式通信奠定了思想基础,虽然已经被淘汰了,但是掌握他的思想还是很重要的,只是基于java,不支持跨语言,服务注册只会注册到RMI Restory注册中心,如果注册中心挂掉了以后,因为不支持负载, 导致所有的客户端都不可以用了,序列化是java原生的序列化,效率不高;服务器底层采用 的是BIO,综合性能不是很高;不支持重试机制,如果网络异常,不会重试,而是直接抛异常;


三、手写RMI框架

TransMessage
TransMessage_Skeleton
TransMessage_Stub
TransMessageClient
TransMessageServer
package com.gupao.vip.RMI.sourceparse;

import java.io.IOException;

public class TransMessage {

    private String content;

    public String getContent() throws IOException, ClassNotFoundException {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}


package com.gupao.vip.RMI.sourceparse;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * serversocket
 *
 */
public class TransMessage_Skeleton extends Thread{

    private TransMessageServer transMessageServer;

    public TransMessage_Skeleton(TransMessageServer transMessageServer) {
        this.transMessageServer = transMessageServer;
    }

    @Override
    public void run() {
        ServerSocket serverSocket=null;
        try {
            serverSocket=new ServerSocket(8888);

            Socket socket=serverSocket.accept();

            while(socket!=null){
                ObjectInputStream read=new ObjectInputStream(socket.getInputStream());

                String method=(String)read.readObject();

                if(method.equals("content")){
                    String content = transMessageServer.getContent();
                    ObjectOutputStream outputStream=new ObjectOutputStream(socket.getOutputStream());
                    outputStream.writeObject(content);
                    outputStream.flush();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


package com.gupao.vip.RMI.sourceparse;


import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

/**
 */
public class TransMessage_Stub extends TransMessage {

    private Socket socket;

    public TransMessage_Stub() throws IOException {
        socket=new Socket("localhost",8888);
    }

    public String getContent() throws IOException, ClassNotFoundException {
        ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
        outputStream.writeObject("content");
        outputStream.flush();

        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
        String content = (String)objectInputStream.readObject();
        return content;
    }
}


package com.gupao.vip.RMI.sourceparse;

import java.io.IOException;

/**
 */
public class TransMessageClient {

    public static void main(String[] args) throws IOException {
        TransMessage transMessage =new TransMessage_Stub();

        String content= null;
        try {
            content = transMessage.getContent();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println(content);
    }
}

package com.gupao.vip.RMI.sourceparse;

/**
 */
public class TransMessageServer extends TransMessage {

    public static void main(String[] args) {
        TransMessageServer userServer=new TransMessageServer();
        userServer.setContent("hello world");

        TransMessage_Skeleton skel=new TransMessage_Skeleton(userServer);

        skel.start();
    }

}


猜你喜欢

转载自blog.csdn.net/Z0157/article/details/82049975