作者:allenn2012 | 来源:互联网 | 2023-08-21 13:08
背景Distributedsystemsrequirethatcomputationsrunningindifferentaddressspaces,potentiallyond
背景
Distributed systems require that computations running in different address spaces, potentially on different hosts, be able to communicate. For a basic communication mechanism, the JavaTM programming language supports sockets, which are flexible and sufficient for general communication. However, sockets require the client and server to engage in applications-level protocols to encode and decode messages for exchange, and the design of such protocols is cumbersome and can be error-prone.
An alternative to sockets is Remote Procedure Call (RPC), which abstracts the communication interface to the level of a procedure call. Instead of working directly with sockets, the programmer has the illusion of calling a local procedure, when in fact the arguments of the call are packaged up and shipped off to the remote target of the call. RPC systems encode arguments and return values using an external data representation, such as XDR.
RPC, however, does not translate well into distributed object systems, where communication between program-level objects residing in different address spaces is needed. In order to match the semantics of object invocation, distributed object systems require remote method invocation or RMI. In such systems, a local surrogate (stub) object manages the invocation on a remote object.
谷歌翻译:分布式系统要求在不同地址空间(可能在不同主机上)运行的计算能够进行通信。对于基本通信机制,JavaTM编程语言支持套接字,这些套接字灵活且足以进行一般通信。然而,套接字要求客户端和服务器参与应用程序级协议以对消息进行编码和解码以进行交换,并且这种协议的设计是麻烦的并且可能容易出错。
套接字的替代方法是远程过程调用(RPC),它将通信接口抽象到过程调用的级别。程序员不是直接使用套接字,而是具有调用本地过程的错觉,实际上调用的参数被打包并运送到远程调用目标。 RPC系统使用外部数据表示(例如XDR)对参数和返回值进行编码。
但是,RPC不能很好地转换为分布式对象系统,其中需要驻留在不同地址空间中的程序级对象之间的通信。为了匹配对象调用的语义,分布式对象系统需要远程方法调用或RMI。在此类系统中,本地代理(存根)对象管理远程对象上的调用。
Java RMI,即 远程方法调使用(Remote Method Invocation),
It is a mechanism that allows an object residing in one system (JVM) to access/invoke an object running on another JVM.
RMI大大增强了java开发分布式应用的能力,例如可以将计算方法复杂的程序放在其他的服务器上,主服务器只需要去调用,而真正的运算是在其他服务器上进行,最后将运算结果返回给主服务器,这样就减轻了主服务器的负担,提高了效率(但是也有其他的开销)。
一种使用于实现远程过程调使用(RPC)(Remote procedure call)的Java API, 可以直接传输序列化后的Java对象和分布式垃圾收集。它的实现依赖于Java虚拟机(JVM),因而它仅支持从一个JVM到另一个JVM的调使用。
in an RMI application, we write two programs, a server program (resides on the server) and a client program (resides on the client).
Inside the server program, a remote object is created and reference of that object is made available for the client (using the registry).
The client program requests the remote objects on the server and tries to invoke its methods.
框架:
Transport Layer − This layer connects the client and the server. It manages the existing connection and also sets up new connections.
Stub − A stub is a representation (proxy) of the remote object at client. It resides in the client system; it acts as a gateway for the client program.
Skeleton − This is the object which resides on the server side. stub communicates with this skeleton to pass request to the remote object.
RRL(Remote Reference Layer) − It is the layer which manages the references made by the client to the remote object.
Working of an RMI Application
The following points summarize how an RMI application works −
When the client makes a call to the remote object, it is received by the stub which eventually passes this request to the RRL.
When the client-side RRL receives the request, it invokes a method called invoke() of the object remoteRef. It passes the request to the RRL on the server side.
The RRL on the server side passes the request to the Skeleton (proxy on the server) which finally invokes the required object on the server.
The result is passed all the way back to the client.
举例
1、接口定义
远程对象的能力是由在客户端和服务器之间共享的接口所表示的:
package rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Warehouse extends Remote
{
double getPrice(String description) throws RemoteException;
}
远程对象的接口必须扩展Remote接口,它位于java.rmi包中。接口中所有的方法必须声明抛出RemoteException异常。这是因为远程方法总是存在失败的可能,所以java编程语言要求每一次远程方法的调用都必须捕获RemoteException,并且指明当调用不成功时应执行的相应处理操作。
2、接口的实现
package rmi;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;
public class WarehouseImpl extends UnicastRemoteObject implements Warehouse
{
private static final long serialVersiOnUID= 1L;
private Map prices;
protected WarehouseImpl() throws RemoteException
{
prices = new HashMap();
prices.put("mate7",3700.00);
}
public double getPrice(String description) throws RemoteException
{
Double price = prices.get(description);
return price == null? 0 : price;
}
}
你可以看出这个类是远程方法调用的目标,因为它扩展自UnicastRemoteObject,这个类的构造器使得它的对象可供远程访问。
3、RMI注册表:通过JNDI发布RMI服务
1、要访问服务器上的一个远程对象时,客户端必须先得到一个本地的存根对象,也就是客户端机器上的代理对象。那么问题来了,如何才能得到这个存根呢?
2、为此,JDK提供了自举注册服务(bootstrap registry service),服务器程序应该使用自举注册服务来注册至少一个远程对象。
3、而要注册一个远程对象,需要一个RMI URL和一个对实现对象的引用。
4、RMI 的URL以rmi:开头,后接域名或IP地址(host),紧接着是端口号(port),最后是服务名(service)。
如:rmi://regserver.mycompany.cmo:99/central_warehouse
如果我们是在本地发布RMI服务,那么host就是“localhost”,此外RMI默认的端口号是“1099”,当然我们也可以自行设置,只要不与其他端口重复即可。 service实际上是基于同一个host与port下唯一的服务名。
发布RMI服务:
1 package rmi;
2
3 import java.net.MalformedURLException;
4 import java.rmi.AlreadyBoundException;
5 import java.rmi.Naming;
6 import java.rmi.RemoteException;
7 import java.rmi.registry.LocateRegistry;
8
9 import javax.naming.NamingException;
10
11
12 public class WarehouseServer
13 {
14 public static void main(String[] args) throws RemoteException, NamingException, MalformedURLException, AlreadyBoundException
15 {
16
17 WarehouseImpl centralWarehouse = new WarehouseImpl();
18
19 //创建注册表
20 LocateRegistry.createRegistry(1099);
//bind方法绑定了RMI地址与RMI服务实现类。发布服务
21 Naming.bind("rmi://localhost:1099/central_warehoues",centralWarehouse);
22
23
24 }
25 }
4、调用RMI服务
1 package rmi;
2
3 import java.net.MalformedURLException;
4 import java.rmi.Naming;
5 import java.rmi.NotBoundException;
6 import java.rmi.RemoteException;
7 import javax.naming.NamingException;
8
9 public class WarehouseClient
10 {
11 public static void main(String[] args) throws NamingException, RemoteException, MalformedURLException, NotBoundException
12 {
13
14 String url = "rmi://localhost:1099/central_warehoues";
15 Warehouse centralWarehouse = (Warehouse) Naming.lookup(url);//centralWarehouse相当于stub
16 String descr = "mate7";
17 double price = centralWarehouse.getPrice(descr);
18 System.out.println(descr + ":" + price);
19 }
20 }
- 、借助JNDI这个所谓的命名与目录服务,我们成功地发布并调用了RMI服务。实际上,JNDI就是一个注册表,服务端将服务对象放入到注册表中,客户端从注册表中获取服务对象。
- 2、在服务端我们发布了RMI服务,并在JNDI中进行了注册,此时就在服务端创建了一个Skeleton(骨架),当客户端第一次成功连接JNDI并获取远程服务对象后,立马在本地创建了一个Stub(存根)。
- 3、远程通信实际是通过Skeleton与Stub来完成的,数据是基于TCP/IP协议,在“传输层”上发送的。
- 4、毋庸置疑,理论上RMI一定比WebService要快,毕竟WebService是基于http协议的,而http所携带的数据是通过“应用层”来传输的。传输层较应用层更为底层,越底层越快。
RMI的局限性
1、只能实现JAVA系统之间的调用,而WebService可以实现跨语言实现系统之间的调用。
2、RMI使用了JAVA默认的序列化方式,对于性能要求比较高的系统,可能需要其他的序列化方案来解决。
3、RMI服务在运行时难免会存在故障,例如,如果RMI服务无法连接了,就会导致客户端无法响应的现象。
资料来源:
Java Remote Method Invocation Specification
分布式架构基础:Java RMI详解-新闻热点
对JAVA RMI的认识 - 冬瓜蔡 - 博客园
https://www.tutorialspoint.com/java_rmi/java_rmi_introduction.htm