使用 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("连接成功");
}
}