当前位置: 首页 > 科技观察

Java中RMI的使用

时间:2023-03-13 11:51:34 科技观察

本文转载自微信公众号《未读码》,作者为未读君,转载本文请联系未读码公众号。RMI简介RMI(远程方法调用)模型是一种分布式对象应用程序。使用RMI技术,一个JVM中的对象可以调用另一个JVM中的对象方法,并获得调用结果。这里另一个JVM可以在同一台计算机或远程计算机上。因此,RMI意味着需要一个Server和一个Client。服务器通常会创建一个对象并使其可远程访问。该对象称为远程对象。服务器端需要注册这个对象才能被客户端远程访问。客户端调用可远程访问的对象上的方法,客户端可以与服务器通信,互相传递信息。说到这里,你是不是觉得用RMI构建分布式应用程序很方便?和RPC一样,可以实现分布式应用之间的相互通信,甚至和现在的微服务思想非常相似。RMI的工作原理就是所谓的“知其然,知其所以然”。在开始编写RMI代码之前,有必要了解一下RMI的工作原理。RMI中客户端如何与服务器端通信?下图可以帮助我们理解RMI的工作流程。RMIConnection从图中可以看出,在客户端有一个叫做Stub的东西,有时也被称为存根。它是RMIClient的代理对象。Stub的主要功能是在请求远程方法时构造一个信息块。RMI协议这个信息块会被发送到Server端。这个信息块由几个部分组成:远程对象标识符。要调用的方法描述。编组参数值(RMI协议中使用对象序列化)。由于客户端有一个可以构造信息发送给服务器的存根,因此服务器必须有一个接收信息的对象,称为骨架。它的主要工作是解析信息块中的调用对象标识和方法描述,在服务端调用具体的对象方法。获取调用的返回值或异常值。整理返回值并将其返回给客户端存根。这里可以获取到客户端调用服务端的结果。RMI开发通过上面的介绍,我们知道了RMI的概念和RMI的工作原理。下面介绍RMI的开发过程。这里用一个场景来演示,假设客户端需要查询用户信息,而用户信息存在于服务端,所以在服务端开放RMI协议接口,供客户端调用查询。RMIServer服务器端主要构造一个可以传输的类User,和一个可以远程访问的类UserService。同时,这个对象必须向RMI注册并向客户端开放。定义服务器接口(需要继承Remote类,方法需要抛出RemoteException)。packagecom.wdbyte.rmi.server;importjava.rmi.Remote;importjava.rmi.RemoteException;/***RMIServer**@authorwww.wdbyte.com*@date2021/05/08*/publicinterfaceUserServiceextendsRemote{/***查找用户**@paramuserId*@return*@throwsRemoteException*/UserfindUser(StringuserId)throwsRemoteException;}步骤3中定义的用户对象,实现服务端接口(需要继承UnicastRemoteObject类,实现定义的接口)。packagecom.wdbyte.rmi.server;importjava.rmi.RemoteException;importjava.rmi.server.UnicastRemoteObject;/***@authorwww.wdbyte.com*@date2021/05/08*/publicclassUserServiceImplextendsUnicastRemoteObjectimplementsUserService{protectedUserServiceImpl()throwsRemoteException{}覆盖publicUserfindUser(StringuserId)throwsRemoteException{//查询中加载if("00001".equals(userId)){Useruser=newUser();user.setName("金庸");user.setAge(100);user.setSkill("写");returnuser;}thrownewRemoteException("没有这个人");}}定义要传输的对象,要传输的对象需要实现可序列化(Serializable)接口。需要传输的类必须实现序列化接口,否则传输时会报错。如何在IDEA中生成serialVersionUID,文末还附上了一个简单的教程。packagecom.wdbyte.rmi.server;importjava.io.Serializable;/****@authorwww.wdbyte.com*@date2021/05/08*/publicclassUserimplementsSerializable{privatestaticfinallongserialVersionUID=6490921832856589236L;privateStringname;privateIntStringeskillage;privatereturnname;}publicvoidsetName(Stringname){this.name=name;}publicIntegergetAge(){returnage;}publicvoidsetAge(Integerage){this.age=age;}publicStringgetSkill(){returnskill;}publicvoidsetSkill(Stringskill){this.skill=skill;}@OverridepublicStringtoString(){return"User{"+"name='"+name+'\''+",age="+age+",skill='"+skill+'\''+'}';}}注册(rmiregistry)远程对象,并启动服务器程序。服务器作为远程访问对象绑定到UserService对象,端口在启动时设置为1900。packagecom.wdbyte.rmi.server;importjava.rmi.Naming;importjava.rmi.registry.LocateRegistry;/***RMIServer**@authorhttps://www.wdbyte.com*@date2021/05/08*/publicclassRmiServer{publicstaticvoidmain(String[]args){try{UserServiceuserService=newUserServiceImpl();LocateRegistry.createRegistry(1900);Naming.rebind("rmi://localhost:1900/user",userService);System.out.println("startserver,portis1900");}catch(Exceptione){e.printStackTrace();}}}RMI客户端比服务器简单得多。直接引入可以远程访问需要转发的类,通过Server绑定的端口和地址发起调用。packagecom.wdbyte.rmi.client;importjava.rmi.Naming;importcom.wdbyte.rmi.server.User;importcom.wdbyte.rmi.server.UserService;/***@authorhttps://www.wdbyte.com*@date2021/05/08*/publicclassRmiClient{publicstaticvoidmain(Stringargs[]){Useranswer;StringuserId="00001";try{//lookupmethodtofindreferenceofremoteobjectUserServiceaccess=(UserService)Naming.lookup("rmi://localhost:1900/user");answer=access.findUser(userId);System.out.println("query:"+userId);System.out.println("result:"+answer);}catch(Exceptionae){System.out.println(ae);}}}RMI测试启动服务器端。startserver,portis1900启动客户端。query:00001result:User{name='金庸',age=100,skill='writing'}如果客户端传入一个不存在的userId。java.rmi.ServerException:RemoteExceptionoccurredinserverthread;nestedexceptionis:java.rmi.RemoteException:NosuchpersonfoundSerialVersionUIDgeneration在IDEA中生成serialVersionUID,打开设置,如下图勾选。IDEA设置选择生成serialVersionUID的类,按智能提示快捷键。IDEAserialVersionUID参考[1]https://docs.oracle.com/javase/tutorial/rmi/overview.html