Java RMI技術詳解與案例分析

来源:https://www.cnblogs.com/wgjava/p/18343209
-Advertisement-
Play Games

Java RMI(Remote Method Invocation)是一種允許Java虛擬機之間進行通信和交互的技術。它使得遠程Java對象能夠像本地對象一樣被訪問和操作,從而簡化了分散式應用程式的開發。一些應用依然會使用 RMI 來實現通信和交互,今天的內容我們來聊聊 RMI 的那些事兒。 一、先 ...


Java RMI(Remote Method Invocation)是一種允許Java虛擬機之間進行通信和交互的技術。它使得遠程Java對象能夠像本地對象一樣被訪問和操作,從而簡化了分散式應用程式的開發。一些應用依然會使用 RMI 來實現通信和交互,今天的內容我們來聊聊 RMI 的那些事兒。

一、先來瞭解一下概念

RMI原理

RMI的基本思想是遠程方法調用。客戶端調用遠程方法時,實際上是發送一個調用請求到伺服器,由伺服器執行該方法,並將結果返回給客戶端。RMI通過存根(Stub)和骨架(Skeleton)類來實現遠程調用,存根位於客戶端,而骨架位於伺服器端。

RMI組件

  1. 遠程介面:必須繼承自java.rmi.Remote介面,並聲明拋出RemoteException
  2. 遠程對象:實現了遠程介面的類。
  3. RMI伺服器:提供遠程對象,並處理客戶端的調用請求。
  4. RMI客戶端:發起遠程方法調用請求。
  5. 註冊服務(Registry):提供服務註冊與獲取,類似於目錄服務。

數據傳遞

RMI使用Java序列化機制來傳遞數據。客戶端將方法參數序列化後通過網路發送給伺服器,伺服器反序列化參數並執行遠程方法,然後將結果序列化回傳給客戶端。

RMI案例

以下是一個簡單的RMI案例,包括伺服器和客戶端的實現思路,下文V 將再用代碼來解釋:

伺服器端

  1. 實現一個遠程介面,例如PersonController,包含一個遠程方法queryName
  2. 創建該介面的具體實現類PersonControllerImpl,併在其中實現遠程方法。
  3. 在伺服器的main方法中,實例化遠程對象,創建RMI註冊表,並使用Naming.rebind將遠程對象綁定到指定名稱。

客戶端

  1. 通過Naming.lookup方法,使用RMI註冊表提供的名稱獲取遠程對象的存根。
  2. 調用存根上的方法,就像調用本地方法一樣,實際上是在調用伺服器上的遠程方法。

RMI的局限性

  • 語言限制:RMI是Java特有的技術,不能直接用於非Java應用程式。
  • 安全性問題:RMI的序列化機制可能帶來安全風險,不建議將1099埠暴露在公網上。
  • 性能和擴展性:RMI的性能受網路延遲和帶寬影響,且在高併發情況下可能面臨擴展性限制。

RMI的應用場景

RMI適用於需要Java程式之間進行遠程通信的場景,如分散式銀行系統、游戲伺服器、股票交易系統和網上商城等。接下來一起看一個簡單的案例使用吧。

二、案例使用

先來搞一個簡單的Java RMI伺服器端和客戶端的實現案例。這個案例中,伺服器端將提供一個名為HelloWorld的遠程服務,客戶端將調用這個服務並列印返回的問候語。

伺服器端實現

  1. 定義遠程介面
    伺服器和客戶端都需要這個介面。它必須繼承自java.rmi.Remote介面,並且所有遠程方法都要聲明拋出RemoteException
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface HelloWorld extends Remote {
    String sayHello() throws RemoteException;
}
  1. 實現遠程介面
    創建一個實現了上述介面的類,並實現遠程方法。
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;

public class HelloWorldImpl extends UnicastRemoteObject implements HelloWorld {
    protected HelloWorldImpl() throws RemoteException {
        super();
    }

    @Override
    public String sayHello() throws RemoteException {
        return "Hello, World!";
    }
}
  1. 設置RMI伺服器
    創建一個主類來設置RMI伺服器,綁定遠程對象到RMI註冊表。
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class HelloWorldServer {
    public static void main(String[] args) {
        try {
            // 創建遠程對象
            HelloWorld helloWorld = new HelloWorldImpl();
            // 獲取RMI註冊表的引用,併在指定埠上創建或獲取註冊表實例
            LocateRegistry.createRegistry(1099);
            // 將遠程對象綁定到RMI註冊表中,客戶端可以通過這個名字訪問遠程對象
            Naming.bind("rmi://localhost/HelloWorld", helloWorld);
            System.out.println("HelloWorld RMI object bound");
        } catch (Exception e) {
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        }
    }
}

客戶端實現

  1. 調用遠程服務
    客戶端使用RMI註冊表的名字來查找遠程對象,並調用其方法。
import java.rmi.Naming;
import java.rmi.RemoteException;

public class HelloWorldClient {
    public static void main(String[] args) {
        try {
            // 使用RMI註冊表的名字查找遠程對象
            HelloWorld helloWorld = (HelloWorld) Naming.lookup("rmi://localhost/HelloWorld");
            // 調用遠程方法
            String response = helloWorld.sayHello();
            System.out.println("Response: " + response);
        } catch (Exception e) {
            System.err.println("Client exception: " + e.toString());
            e.printStackTrace();
        }
    }
}

來詳細解釋吧

  • 遠程介面 (HelloWorld): 這是伺服器和客戶端之間通信的協議。它定義了可以被遠程調用的方法。
  • 遠程對象實現 (HelloWorldImpl): 這是遠程介面的一個實現。RMI調用實際上會調用這個實現中的方法。
  • 伺服器 (HelloWorldServer): 負責創建遠程對象的實例,並將這個實例綁定到RMI註冊表中。這樣客戶端就可以通過註冊表的名字來訪問這個對象。
  • 客戶端 (HelloWorldClient): 使用RMI註冊表的名字來查找伺服器上的遠程對象,並調用其方法。

接下來就可以編譯所有類文件,運行伺服器端程式,確保RMI註冊表已經啟動(在某些Java版本中會自動啟動),再運行客戶端程式,搞定。註意一下哈,由於RMI使用Java序列化機制,因此客戶端和伺服器的類路徑必須一致或相容。

三、RMI 在分散式銀行系統中的應用

接下來V哥要介紹業務場景下的應用了,拿在分散式銀行系統中來說,我們可以使用RMI來實現不同銀行分行之間的通信,例如,實現賬戶信息的查詢、轉賬等操作。以下是一個簡化的示例,其中包括兩個基本操作:查詢賬戶餘額和執行轉賬,按步驟一步一步來吧。

步驟1: 定義遠程介面

首先,定義一個遠程介面BankService,它將被各個分行實現以提供銀行服務。

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface BankService extends Remote {
    double getAccountBalance(String accountNumber) throws RemoteException;
    boolean transferFunds(String fromAccount, String toAccount, double amount) throws RemoteException;
}

步驟2: 實現遠程介面

接下來,實現這個介面來創建遠程對象,這個對象將提供實際的銀行服務。

import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;

public class BankServiceImpl extends UnicastRemoteObject implements BankService {
    private Map<String, Double> accounts = new HashMap<>();

    protected BankServiceImpl() throws RemoteException {
        super();
        // 初始化一些賬戶信息
        accounts.put("123456789", 5000.00);
        accounts.put("987654321", 1000.00);
    }

    @Override
    public double getAccountBalance(String accountNumber) throws RemoteException {
        return accounts.getOrDefault(accountNumber, 0.00);
    }

    @Override
    public boolean transferFunds(String fromAccount, String toAccount, double amount) throws RemoteException {
        if (accounts.containsKey(fromAccount) && accounts.get(fromAccount) >= amount) {
            accounts.put(fromAccount, accounts.get(fromAccount) - amount);
            accounts.merge(toAccount, amount, Double::sum);
            return true;
        }
        return false;
    }
}

步驟3: 設置RMI伺服器

伺服器端將創建BankService的遠程對象實例,並將其綁定到RMI註冊表中。

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class BankServer {
    public static void main(String[] args) {
        try {
            LocateRegistry.createRegistry(1099); // 創建RMI註冊表
            BankService bankService = new BankServiceImpl();
            Naming.rebind("//localhost/BankService", bankService); // 綁定遠程對象
            System.out.println("BankService is ready for use.");
        } catch (Exception e) {
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        }
    }
}

步驟4: 實現RMI客戶端

客戶端將使用RMI註冊表的名字來查找遠程對象,並調用其方法。

import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class BankClient {
    public static void main(String[] args) {
        try {
            BankService bankService = (BankService) Naming.lookup("//localhost/BankService");
            System.out.println("Account balance: " + bankService.getAccountBalance("123456789"));
            
            // 執行轉賬操作
            boolean isTransferSuccess = bankService.transferFunds("123456789", "987654321", 200.00);
            if (isTransferSuccess) {
                System.out.println("Transfer successful.");
            } else {
                System.out.println("Transfer failed.");
            }
            
            // 再次查詢餘額
            System.out.println("New account balance: " + bankService.getAccountBalance("123456789"));
        } catch (RemoteException | NotBoundException e) {
            System.err.println("Client exception: " + e.toString());
            e.printStackTrace();
        }
    }
}

來詳細解釋一下

  • 遠程介面 (BankService): 定義了兩個方法:getAccountBalance用於查詢賬戶餘額,transferFunds用於執行轉賬操作。
  • 遠程對象實現 (BankServiceImpl): 實現了BankService介面。它使用一個HashMap來模擬賬戶和餘額信息。
  • 伺服器 (BankServer): 設置了RMI伺服器,將BankService的實現綁定到RMI註冊表中,供客戶端訪問。
  • 客戶端 (BankClient): 查找RMI註冊表中的BankService服務,並調用其方法來查詢餘額和執行轉賬。

擼完代碼後,編譯所有類文件,運行伺服器端程式BankServer,再運行客戶端程式BankClient,測試效果吧。

最後

最後V哥要提醒一下,在實際的銀行系統中,當然還需要考慮安全性、事務性、持久性以及錯誤處理等多方面的因素,RMI的網路通信也需要在安全的網路環境下進行,以防止數據泄露或被篡改。你在應用中是怎麼使用 RMI 的,歡迎關註威哥愛編程,一起交流一下哈。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 寫在前面 前面給了關於java方法和數組的十題編程題,如果你能有思路很快速地完成它,說明你這部分的基礎知識很好,接下來就來看看後面的面向對象的相關知識吧! 面向對象 概述:不斷地創建對象,使用對象,指揮對象做事情的思想。 類和對象的關係: 類: 是java的基本單位,主要使用用於描述現實生活的事物。 ...
  • # 字元串長度 - strlen() 描述 C 庫函數 size_t strlen(const char *str) 計算字元串 str 的長度,直到空結束字元,但不包括空結束字元。 聲明 下麵是 strlen() 函數的聲明。 size_t strlen(const char *str) 參數 s ...
  • P1223 排隊接水 題目描述 有 \(n\) 個人在一個水龍頭前排隊接水,假如每個人接水的時間為 \(T_i\),請編程找出這 \(n\) 個人排隊的一種順序,使得 \(n\) 個人的平均等待時間最小。 輸入格式 第一行為一個整數 \(n\)。 第二行 \(n\) 個整數,第 \(i\) 個整數 ...
  • 將Word文檔以圖片形式導出,既能方便信息的分享,也能保護數據安全,避免被二次編輯。文本將介紹如何使用 Spire.Doc for Python 庫在Python程式中實現Word到圖片的批量轉換。 Python 將Word轉換為JPG、JPEG、PNG、BMP等圖片格式 Python 將Word文 ...
  • 安裝Nginx並配置訪問 安裝PHP並輸出腳本結果 配置typecho Nginx安裝並驗證 apt install nginx systemctl start nginx 正常情況應該可以看到Nginx的歡迎頁面了,如果看不到就是防火牆的問題,設置下防火牆放通即可。 安裝PHP並使用Nginx代理 ...
  • 堆(Heap)的基本概念 堆是一種完全二叉樹(Complete Binary Tree),其性質使得堆可以高效地支持以下操作: 插入(Insert):將一個新元素加入到堆中。 刪除最大/最小元素(Delete Max/Min):移除並返回堆中的最大(大根堆)或最小(小根堆)元素。 獲取最大/最小元素 ...
  • 節點結構體設計 struct LinkNode { // 數據域 void* data; // 指針域 struct LinkNode * next; }; data:一個 void* 類型的指針,指向節點存儲的數據。使用 void* 是為了鏈表能夠存儲不同類型的數據。 next:一個指向下一個 L ...
  • strcat 描述 char *strcat(char *dest, const char *src) 把 src 所指向的字元串追加到 dest 所指向的字元串的結尾。 聲明 下麵是 strcat() 函數的聲明。 char *strcat(char *dest, const char *src) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...