詳解Java 中 RMI 的使用
RMI 介紹
RMI (Remote Method Invocation) 模型是一種分布式對(duì)象應(yīng)用,使用 RMI 技術(shù)可以使一個(gè) JVM 中的對(duì)象,調(diào)用另一個(gè) JVM 中的對(duì)象方法并獲取調(diào)用結(jié)果。這里的另一個(gè) JVM 可以在同一臺(tái)計(jì)算機(jī)也可以是遠(yuǎn)程計(jì)算機(jī)。因此,RMI 意味著需要一個(gè) Server 端和一個(gè) Client 端。
Server 端通常會(huì)創(chuàng)建一個(gè)對(duì)象,并使之可以被遠(yuǎn)程訪問。
這個(gè)對(duì)象被稱為遠(yuǎn)程對(duì)象。Server 端需要注冊(cè)這個(gè)對(duì)象可以被 Client 遠(yuǎn)程訪問。
Client 端調(diào)用可以被遠(yuǎn)程訪問的對(duì)象上的方法,Client 端就可以和 Server 端進(jìn)行通信并相互傳遞信息。
說到這里,是不是發(fā)現(xiàn)使用 RMI 在構(gòu)建一個(gè)分布式應(yīng)用時(shí)十分方便,它和 RPC 一樣可以實(shí)現(xiàn)分布式應(yīng)用之間的互相通信,甚至和現(xiàn)在的微服務(wù)思想都十分類似。
RMI 工作原理
正所謂 “知其然知其所以然”,在開始編寫 RMI 代碼之前,有必要了解一下 RMI 的工作原理,RMI 中 Client 端是和 Server 端是如何通信的呢?
下圖的可以幫助我們理解RMI 的工作流程。
從圖中可以看到,Client 端有一個(gè)被稱 Stub 的東西,有時(shí)也會(huì)被成為存根,它是 RMI Client 的代理對(duì)象,Stub 的主要功能是請(qǐng)求遠(yuǎn)程方法時(shí)構(gòu)造一個(gè)信息塊,RMI 協(xié)議會(huì)把這個(gè)信息塊發(fā)送給 Server 端。
這個(gè)信息塊由幾個(gè)部分組成:
- 遠(yuǎn)程對(duì)象標(biāo)識(shí)符。
- 調(diào)用的方法描述。
- 編組后的參數(shù)值(RMI協(xié)議中使用的是對(duì)象序列化)。
既然 Client 端有一個(gè) Stub 可以構(gòu)造信息塊發(fā)送給 Server 端,那么 Server 端必定會(huì)有一個(gè)接收這個(gè)信息快的對(duì)象,稱為 Skeleton 。
它主要的工作是:
- 解析信息快中的調(diào)用對(duì)象標(biāo)識(shí)符和方法描述,在 Server 端調(diào)用具體的對(duì)象方法。
- 取得調(diào)用的返回值或者異常值。
- 把返回值進(jìn)行編組,返回給客戶端 Stub.
到這里,一次從 Client 端對(duì) Server 端的調(diào)用結(jié)果就可以獲取到了。
RMI 開發(fā)
通過上面的介紹,知道了 RMI 的概念以及 RMI 的工作原理,下面介紹 RMI 的開發(fā)流程。
這里會(huì)通過一個(gè)場(chǎng)景進(jìn)行演示,假設(shè) Client 端需要查詢用戶信息,而用戶信息存在于 Server 端,所以在 Server 端開放了 RMI 協(xié)議接口供客戶端調(diào)用查詢。
RMI Server
Server 端主要是構(gòu)建一個(gè)可以被傳輸?shù)念?User,一個(gè)可以被遠(yuǎn)程訪問的類 UserService,同時(shí)這個(gè)對(duì)象要注冊(cè)到 RMI 開放給客戶端使用。
1.定義服務(wù)器接口(需要繼承 Remote 類,方法需要拋出 RemoteException)。
package com.wdbyte.rmi.server; import java.rmi.Remote; import java.rmi.RemoteException; /** * RMI Server * * @author www.wdbyte.com * @date 2021/05/08 */ public interface UserService extends Remote { /** * 查找用戶 * * @param userId * @return * @throws RemoteException */ User findUser(String userId) throws RemoteException; }
User 對(duì)象在步驟 3 中定義。
2.實(shí)現(xiàn)服務(wù)器接口(需要繼承 UnicastRemoteObject 類,實(shí)現(xiàn)定義的接口)。
package com.wdbyte.rmi.server; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; /** * @author www.wdbyte.com * @date 2021/05/08 */ public class UserServiceImpl extends UnicastRemoteObject implements UserService { protected UserServiceImpl() throws RemoteException { } @Override public User findUser(String userId) throws RemoteException { // 加載在查詢 if ("00001".equals(userId)) { User user = new User(); user.setName("金庸"); user.setAge(100); user.setSkill("寫作"); return user; } throw new RemoteException("查無此人"); } }
3.定義傳輸?shù)膶?duì)象,傳輸?shù)膶?duì)象需要實(shí)現(xiàn)序列化(Serializable)接口。
需要傳輸?shù)念愐欢ㄒ獙?shí)現(xiàn)序列化接口,不然傳輸時(shí)會(huì)報(bào)錯(cuò)。IDEA 中如何生成 serialVersionUID,在文章末尾也附上了簡(jiǎn)單教程。
package com.wdbyte.rmi.server; import java.io.Serializable; /** * * @author www.wdbyte.com * @date 2021/05/08 */ public class User implements Serializable { private static final long serialVersionUID = 6490921832856589236L; private String name; private Integer age; private String skill; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getSkill() { return skill; } public void setSkill(String skill) { this.skill = skill; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", skill='" + skill + '\'' + '}'; } }
4.注冊(cè)( rmiregistry)遠(yuǎn)程對(duì)象,并啟動(dòng)服務(wù)端程序。
服務(wù)端綁定了 UserService 對(duì)象作為遠(yuǎn)程訪問的對(duì)象,啟動(dòng)時(shí)端口設(shè)置為 1900。
package com.wdbyte.rmi.server; import java.rmi.Naming; import java.rmi.registry.LocateRegistry; /** * RMI Server 端 * * @author https://www.wdbyte.com * @date 2021/05/08 */ public class RmiServer { public static void main(String[] args) { try { UserService userService = new UserServiceImpl(); LocateRegistry.createRegistry(1900); Naming.rebind("rmi://localhost:1900/user", userService); System.out.println("start server,port is 1900"); } catch (Exception e) { e.printStackTrace(); } } }
RMI Client
相比 Server 端,Client 端就簡(jiǎn)單的多。直接引入可遠(yuǎn)程訪問和需要傳輸?shù)念?,通過端口和 Server 端綁定的地址,就可以發(fā)起一次調(diào)用。
package com.wdbyte.rmi.client; import java.rmi.Naming; import com.wdbyte.rmi.server.User; import com.wdbyte.rmi.server.UserService; /** * @author https://www.wdbyte.com * @date 2021/05/08 */ public class RmiClient { public static void main(String args[]) { User answer; String userId = "00001"; try { // lookup method to find reference of remote object UserService access = (UserService)Naming.lookup("rmi://localhost:1900/user"); answer = access.findUser(userId); System.out.println("query:" + userId); System.out.println("result:" + answer); } catch (Exception ae) { System.out.println(ae); } } }
RMI 測(cè)試
啟動(dòng) Server 端。
start server,port is 1900
啟動(dòng) Client 端。
query:00001 result:User{name='金庸', age=100, skill='寫作'}
如果 Client 端傳入不存在的 userId。
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: java.rmi.RemoteException: 查無此人
serialVersionUID 的生成
IDEA 中生成 serialVersionUID,打開設(shè)置,如下圖所示勾選。
選中要生成 serialVersionUID 的類,按智能提示快捷鍵。
參考
[1] https://docs.oracle.com/javase/tutorial/rmi/overview.html
到此這篇關(guān)于詳解Java 中 RMI 的使用的文章就介紹到這了,更多相關(guān)java RMI使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
visual studio 2019安裝配置可編寫c/c++語言的IDE環(huán)境
這篇文章主要介紹了visual studio 2019安裝配置可編寫c/c++語言的IDE環(huán)境,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03webuploader 實(shí)現(xiàn)圖片批量上傳功能附實(shí)例代碼
這篇文章主要介紹了webuploader 實(shí)現(xiàn)圖片批量上傳功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-11-11SpringBoot?SpringSecurity?JWT實(shí)現(xiàn)系統(tǒng)安全策略詳解
Spring?Security是Spring的一個(gè)核心項(xiàng)目,它是一個(gè)功能強(qiáng)大且高度可定制的認(rèn)證和訪問控制框架。它提供了認(rèn)證和授權(quán)功能以及抵御常見的攻擊,它已經(jīng)成為保護(hù)基于spring的應(yīng)用程序的事實(shí)標(biāo)準(zhǔn)2022-11-11數(shù)組與List之間相互轉(zhuǎn)換的方法詳解
本文是對(duì)數(shù)組與List之間相互轉(zhuǎn)換的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下。希望對(duì)大家有所幫助2013-10-10SpringBoot整合MyBatis實(shí)現(xiàn)CRUD操作項(xiàng)目實(shí)踐
本文主要介紹了SpringBoot整合MyBatis實(shí)現(xiàn)CRUD操作項(xiàng)目實(shí)踐,如何實(shí)現(xiàn)數(shù)據(jù)庫的CRUD創(chuàng)建、讀取、更新、刪除操作,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02SpringMVC中MultipartFile轉(zhuǎn)File的兩種方式
在spring上傳文件中,一般都使用了MultipartFile來接收,但是有需要用到File的地方,本文主要介紹了SpringMVC中MultipartFile轉(zhuǎn)File的兩種方式,感興趣的可以了解一下2022-04-04