關於rmi——各種資料的摘抄
背景
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<String,Double> prices; protected WarehouseImpl() throws RemoteException { prices = new HashMap<String,Double>(); 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就是一個登錄檔,服務端將服務物件放入到登錄檔中,客戶端從登錄檔中獲取服務物件。
- 在服務端我們釋出了RMI服務,並在JNDI中進行了註冊,此時就在服務端建立了一個Skeleton(骨架),當客戶端第一次成功連線JNDI並獲取遠端服務物件後,立馬在本地建立了一個Stub(存根)。
- 遠端通訊實際是通過Skeleton與Stub來完成的,資料是基於TCP/IP協議,在“傳輸層”上傳送的。
- 毋庸置疑,理論上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