使用 Zookeeper 与 RMI 技术实现一个 RPC 框架

使用 Zookeeper 与 RMI 技术实现一个 RPC 框架。

基于 RMI 实现远程方法调用:

1.1RMI 简 介:

ava本身提供了一种RPC框架——RMI(即Remote Method Invoke 远程方法调用),在编写一个接口需要作为远程调用时,都需要继承了Remote,Remote 接口用于标识其方法可以从非本地虚拟机上调用的接口,只有在“远程接口”扩展java.rmi.Remote 的接口)中指定的这些方法才可远程使用。

RMI 是 Java 语言的远程调用,无法实现跨语言。

1.2执行流程

Registry(注册表)是放置所有的服务器对象命名空间。服务端每创建一个方法,都会在Registry中通过bind()或rebind()方法进行注册,并在其中绑定唯一的名称。

客户端要远程调用Service中的方法,需要通过服务端绑定的名称从注册表中获取对象(lookup()方法)。

1.3RMI 的 API 介绍

Remote 接口

java.rmi.Remote 定义了此接口为远程调用接口。如果接口被外部调用,需要继承此接

口。

RemoteException 类

java.rmi.RemoteException 继承了 Remote 接口的接口,如果方法是允许被远程调用的,需要抛出此异常。

UnicastRemoteObject 类

java.rmi.server.UnicastRemoteObject 此类实现了 Remote 接口和 Serializable 接口。

自定义接口实现类除了实现自定义接口还需要继承此类。

LocateRegistry 类

java.rmi.registry.LocateRegistry 可以通过 LocateRegistry 在本机上创建 Registry,通过特定的端口就可以访问这个Registry。

Naming 类

java.rmi.Naming Naming 定义了发布内容可访问 RMI 名称。也是通过 Naming 获取到指定的远程方法。

1.4创建 Server 端

创建项目
在这里插入图片描述

创建接口

远程接口,该接口需要继承Remote接口,并且接口中的方法全都要抛出RemoteException异常

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface UserService extends Remote {
    
    
        String findUsers(String str) throws RemoteException;
}

创建接口实现类

远程接口实现类,必须继承UnicastRemoteObject(继承RemoteServer->继承RemoteObject->实现Remote,Serializable),只有继承UnicastRemoteObject类,才表明其可以作为远程对象,被注册到注册表中供客户端远程调用(补充:客户端lookup找到的对象,只是该远程对象的Stub(存根对象),而服务端的对象有一个对应的骨架Skeleton(用于接收客户端stub的请求,以及调用真实的对象)对应,Stub是远程对象的客户端代理,Skeleton是远程对象的服务端代理,他们之间协作完成客户端与服务器之间的方法调用时的通信。

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class UserServiceImpl extends UnicastRemoteObject implements UserService {
    
    

public UserServiceImpl() throws RemoteException{
    
    
}


    public String findUsers(String str) throws RemoteException {
    
    
        return "Hello Zookeeper" + str;
    }
}


说明:为何HelloImpl继承了UnicastRemoteObject就可以被作为远程对象发布,查阅UnicastRemoteObject的源码可以发现:

    /**
     * Creates and exports a new UnicastRemoteObject object using an
     * anonymous port.
     *
     * <p>The object is exported with a server socket
     * created using the {@link RMISocketFactory} class.
     *
     * @throws RemoteException if failed to export object
     * @since JDK1.1
     */
    protected UnicastRemoteObject() throws RemoteException
    {
    
    
        this(0);
    }

	 /**
     * Creates and exports a new UnicastRemoteObject object using the
     * particular supplied port.
     *
     * <p>The object is exported with a server socket
     * created using the {@link RMISocketFactory} class.
     *
     * @param port the port number on which the remote object receives calls
     * (if <code>port</code> is zero, an anonymous port is chosen)
     * @throws RemoteException if failed to export object
     * @since 1.2
     */
    protected UnicastRemoteObject(int port) throws RemoteException
    {
    
    
        this.port = port;
        exportObject((Remote) this, port);
    }

    /**
     * Creates and exports a new UnicastRemoteObject object using the
     * particular supplied port and socket factories.
     *
     * <p>Either socket factory may be {@code null}, in which case
     * the corresponding client or server socket creation method of
     * {@link RMISocketFactory} is used instead.
     *
     * @param port the port number on which the remote object receives calls
     * (if <code>port</code> is zero, an anonymous port is chosen)
     * @param csf the client-side socket factory for making calls to the
     * remote object
     * @param ssf the server-side socket factory for receiving remote calls
     * @throws RemoteException if failed to export object
     * @since 1.2
     */
    protected UnicastRemoteObject(int port,
                                  RMIClientSocketFactory csf,
                                  RMIServerSocketFactory ssf)
        throws RemoteException
    {
    
    
        this.port = port;
        this.csf = csf;
        this.ssf = ssf;
        exportObject((Remote) this, port, csf, ssf);
    }

其实在启动server端的时候,new了UserServerImpl对象,因为继承了UnicastRemoteObject,会先调用父类的构造方法,这时候,就会将this(当前对象)通过exportObject方法注册。

所以,如果在被导出的对象需要继承其它的类,那么就可以不采用集成UnicastRemoteObject的方式,而是通过exportObject方法将其导出为远程对象:

...
// 创建一个远程对象
UserService userService = new UserServiceImpl();
//UserServiceImpl不需要继承UnicastRemoteObject类,通过exportObject将其显示导出
UnicastRemoteObject.exportObject(userService ,0);
...

编写主方法

服务端启动类,用于创建远程对象注册表以及注册远程对象


import com.hkj.service.UserService;
import com.hkj.service.UserServiceImpl;
import org.apache.zookeeper.*;
import java.io.IOException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

public class ServiceDemo implements Watcher {
    
    

    public static void main(String[] args)  throws IOException, AlreadyBoundException, KeeperException, InterruptedException {
    
    

        UserService userService = new UserServiceImpl();
        LocateRegistry.createRegistry(8888);
        String url = "rmi://localhost:8888/user";
        Naming.bind(url,userService);

        //将url信息放到zookeeper节点中
        ZooKeeper  zooKeeper = new ZooKeeper("192.168.1.111:2181,192.168.1.111:2182,192.168.1.111:2183",150000,new ServiceDemo());

        //创建Znode
        zooKeeper.create("/text/service",url.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println("服务发布成功");

    }

    public void process(WatchedEvent watchedEvent) {
    
    
        if (watchedEvent.getState() == Event.KeeperState.SyncConnected){
    
    
            System.out.println("SUCCESS!");
        }
    }
}

1.5创建Client层

创建项目

在这里插入图片描述

服务端接口

public interface UserService {
    
    
    String findUser(String str);
}

编写主方法

import com.hkj.service.UserService;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.rmi.Naming;
import java.rmi.NotBoundException;

public class ClientDemo implements Watcher {
    
    
    public static void main(String[] args) throws IOException, NotBoundException, KeeperException, InterruptedException {
    
    
        ZooKeeper zooKeeper = new ZooKeeper("192.168.1.111:2181,192.168.1.111:2182,192.168.1.111:2183",
                150000,new ClientDemo());
        byte[] bytes = zooKeeper.getData("/test/service",new ClientDemo(),null);

        String url = new String(bytes);
        UserService userService = (UserService)Naming.lookup(url);

        String result = userService.findUser("Nuchkj");
        System.out.println(result);

    }

    public void process(WatchedEvent watchedEvent) {
    
    
        if(watchedEvent.getState() == Event.KeeperState.SyncConnected);
            System.out.println("连接成功");
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_44192389/article/details/107735071