Java 分布式緩存實現:結合 RMI 與本地文件緩存

目錄

一、核心思路

二、項目結構說明

2.1?服務端項目結構(IDEA)

2.2?客戶端項目結構(Eclipse)

三、服務端實現(IDEA)

3.1 數據庫訪問層

3.2 遠程接口定義

3.3 遠程服務實現

3.4 服務端啟動類

四、客戶端實現(Eclipse)

4.1 緩存攔截器

4.2 代理類

4.3 RMI客戶端

4.4 測試類

五、運行流程與效果

5.1 服務端啟動

5.2 客戶端第一次查詢

5.3 客戶端第二次查詢


????在分布式系統中,緩存是提升性能的關鍵手段。它能減少對遠程服務或數據庫的重復訪問,降低網絡開銷與服務壓力。本文將通過 “RMI 遠程數據查詢 + 本地文件緩存”的組合,實現一套簡單但實用的分布式緩存方案。

一、核心思路

????????當客戶端需要查詢數據時,優先從本地緩存文件讀取;若緩存不存在,再通過 RMI 調用遠程服務查詢數據庫,并將結果寫入本地緩存,供后續使用。整體流程如下:

  1. 遠程服務端:提供數據庫查詢能力,通過 RMI 暴露服務。

  2. 客戶端代理層:攔截數據查詢請求,管理緩存邏輯(讀緩存、寫緩存)。

  3. 緩存存儲:使用本地文件序列化存儲查詢結果。

二、項目結構說明

2.1?服務端項目結構(IDEA)

2.2?客戶端項目結構(Eclipse)

三、服務端實現(IDEA)

3.1 數據庫訪問層

????????數據庫操作類(DBData):負責連接數據庫并執行查詢

package com.demo14.dao;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class DBData  {Connection conn;public void connDB() {try {Class.forName("com.mysql.cj.jdbc.Driver");conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jk202508", "root", "152602");} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}// 查詢表數據,返回List<Map>格式。查詢結果列表(每行數據為Map形式)public List<Map<String, String>> queryDatas(String tableName) {// TODO Auto-generated method stubthis.connDB();String sql = "select  *  from  " + tableName;List<Map<String, String>> lists = new ArrayList<Map<String, String>>();try {PreparedStatement pstmt = this.conn.prepareStatement(sql);ResultSet rs = pstmt.executeQuery();ResultSetMetaData rsmd = rs.getMetaData();int columns = rsmd.getColumnCount();while (rs.next()) {Map<String, String> LineMap = new HashMap<String, String>();for (int i = 0; i < columns; i++) {LineMap.put(rsmd.getColumnName(i + 1), rs.getString(i + 1));}lists.add(LineMap);}} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if (null != conn) {try {conn.close();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}return lists;}}

3.2 遠程接口定義

????????定義 RMI 遠程調用的接口:

  • 繼承Remote接口標識為遠程服務
  • 方法必須聲明拋出RemoteException
  • 接口需要在客戶端和服務端保持一致
package com.demo14.interfaces;import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.List;
import java.util.Map;public interface DataAop  extends Remote {public List<Map<String,String>>  queryDatas(String name) throws RemoteException;}

3.3 遠程服務實現

????????實現遠程接口,調用DBData查詢數據庫:

  • 繼承UnicastRemoteObject,自動將對象導出為 RMI 遠程對象。
  • 構造函數必須拋出RemoteException
  • 調用DBDataqueryDatas方法,實現數據庫查詢。
package com.demo14.impl;import com.demo14.dao.DBData;
import com.demo14.interfaces.DataAop;import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.List;
import java.util.Map;/*** 遠程服務實現類* 繼承UnicastRemoteObject并提供遠程方法實現*/
public class DataAopImpl  extends UnicastRemoteObject implements DataAop {public  DataAopImpl() throws RemoteException {super();}@Overridepublic List<Map<String, String>> queryDatas(String tableName) throws RemoteException {// TODO Auto-generated method stubSystem.out.println("RMI服務器:查詢數據庫表 " + tableName);DBData db  = new DBData();List<Map<String, String>> result =  db.queryDatas(tableName);System.out.println("查詢完成,返回" + result.size() + "條記錄");return result;}}

3.4 服務端啟動類

????????啟動 RMI 服務并注冊遠程對象:

  • LocateRegistry.createRegistry(9200):在端口 9200 啟動 RMI 注冊表。
  • Naming.bind(...):將DataAopImpl實例綁定到 RMI 注冊表,客戶端可通過rmi://127.0.0.1:9200/queryDatas訪問。
package com.demo14.rmiserver;import com.demo14.impl.DataAopImpl;
import com.demo14.interfaces.DataAop;import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;// RMI服務端啟動類
public class Main {public static void main( String[] args ){try {// 1. 創建遠程服務實例DataAop dataAop  = new DataAopImpl();// 2. 啟動RMI注冊表(端口9200)LocateRegistry.createRegistry(9200);System.out.println("RMI注冊表啟動成功,端口: 9200");// 3. 綁定遠程服務到注冊表(Java命名目錄服務)Naming.bind("rmi://127.0.0.1:9200/queryDatas", dataAop);System.out.println("數據服務已就緒,等待客戶端連接...");} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (MalformedURLException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (AlreadyBoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

四、客戶端實現(Eclipse)

4.1 緩存攔截器

????????緩存攔截器(InterceptorData)負責檢查本地緩存文件是否存在:

  • 檢查./cachermidata/目錄下是否存在目標表(tableName)的緩存文件。
  • 若存在,通過ObjectInputStream反序列化緩存數據。
package com.demo14;import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** 緩存攔截器 - 負責檢查本地緩存文件* 實現緩存檢查和加載功能*/
public class InterceptorData {public List checkFile(String tableName) {List<Map<String, String>> lists = null;// 假設有一個緩沖的目錄的存在File file = new File("./cachermidata");File[] fs = file.listFiles();if (fs.length == 0) {System.out.println("該目錄下沒有緩存的目錄數據文件");lists = new ArrayList<Map<String, String>>();} else {System.out.println("該目錄下有緩存的目錄數據文件");for (File f : fs) {if (f.getName().contains(tableName)) {System.out.println("目標數據緩存文件存在");ObjectInputStream objIn = null;try {objIn = new ObjectInputStream(new FileInputStream("./cachermidata/" + tableName + ".datas"));lists = (ArrayList<Map<String, String>>) objIn.readObject();System.out.println("從緩存加載" + lists.size() + "條記錄");break;} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}else{System.out.println("目標數據緩存文件不存在");lists = new ArrayList<Map<String, String>>();}}}return lists;}}

4.2 代理類

????????實現緩存邏輯(先讀緩存,緩存沒有則調用 RMI 并寫緩存):

  • 代理模式:封裝DataAop(RMI 遠程服務)和InterceptorData(緩存攔截)。
  • 緩存邏輯:先查本地緩存,緩存存在則直接返回;否則調用 RMI,再將結果寫入緩存。
package com.demo14;import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.rmi.RemoteException;
import java.util.List;
import java.util.Map;
import java.rmi.Remote;/*** 代理類 - 實現緩存策略的核心* 采用代理模式,在遠程調用前先檢查緩存*/
public class ProxyData implements DataAop {private DataAop dataAop;		// RMI遠程服務private InterceptorData interceptor;	// 緩存攔截public ProxyData(DataAop dataAop, InterceptorData interceptor) {this.dataAop = dataAop;this.interceptor = interceptor;}@Overridepublic List<Map<String, String>> queryDatas(String tableName) {// 檢查本地緩存List lists = this.interceptor.checkFile(tableName);if (lists.size() > 0) {System.out.println("直接從緩存中獲取數據....");return lists;} else {// 緩存未命中,調用RMI查詢遠程服務List<Map<String, String>> dbList = null;try {System.out.println("要去分布式RMI服務器查詢數據....");dbList = this.dataAop.queryDatas(tableName);// 將查詢結果寫入本地緩存ObjectOutputStream objOut = null;objOut = new ObjectOutputStream(new FileOutputStream("./cachermidata/" + tableName + ".datas"));objOut.writeObject(dbList);} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (Exception e) {}return dbList;}}}

4.3 RMI客戶端

????????封裝 RMI 服務的查找邏輯:

  • 通過Naming.lookup從 RMI 注冊表獲取遠程服務引用。
  • 對外暴露與DataAop一致的接口,隱藏 RMI 調用細節。
package com.demo14;import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.List;
import java.util.Map;/*** RMI客戶端 - 負責與遠程服務通信* 實現DataAop接口,提供遠程調用能力*/
public class RMIData   implements DataAop{DataAop dataAop;// 連接RMI服務器public void connRMIServer() {try {dataAop = (DataAop) Naming.lookup("rmi://127.0.0.1:9200/queryDatas");System.out.println("RMI服務器連接成功");} catch (MalformedURLException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (NotBoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}@Overridepublic List<Map<String, String>> queryDatas(String tableName) throws RemoteException {// TODO Auto-generated method stubif (dataAop == null) {this.connRMIServer();}System.out.println("發起遠程查詢: " + tableName);return dataAop.queryDatas(tableName);}}

4.4 測試類

????????用戶交互入口,指定查詢的表名:

package com.demo14;import java.util.List;
import java.util.Map;
import java.util.Scanner;public class Test {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);System.out.println("=== 分布式緩存數據查詢系統 ===");System.out.print("請輸入要查詢的表名: ");String tableName = scanner.nextLine();System.out.print("請輸入要顯示的字段名: ");String keyName = scanner.nextLine();// 創建代理實例(組合了遠程調用和緩存功能)ProxyData proxy = new ProxyData(new RMIData(), new InterceptorData());List<Map<String, String>> result = proxy.queryDatas(tableName);if (result.isEmpty()) {System.out.println("未找到數據或表不存在");} else {// 顯示指定字段的數據for (Map<String, String> row : result) {String value = row.get(keyName);if (value != null) {System.out.println(keyName + ": " + value);}}System.out.println("共 " + result.size() + " 條記錄");}}}

五、運行流程與效果

5.1 服務端啟動

?運行Main類,啟動 RMI 服務:

5.2 客戶端第一次查詢

5.3 客戶端第二次查詢

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/96918.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/96918.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/96918.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Electron第一個應用

1、安裝node nodeJS下載 2、下載完成&#xff0c;需要配置環境。 寫道path路徑 、 3、安裝完成&#xff0c;查看版本 npm -v4、 配置cnpm npm install -g cnpm --registryhttps://registry.npmmirror.com5、參考Electron 寫&#xff1a; Electron第一個程序hello 6、安裝…

React 原理篇 - React 新架構深度解析

使用過 React v16 之前版本的開發者或許都經歷過這樣的場景&#xff1a;當頁面包含復雜組件或大量列表時&#xff0c;輸入框打字會卡頓&#xff0c;滾動會不流暢。這些體驗問題的背后&#xff0c;往往與 React 的渲染機制密切相關。2017 年 React v16 推出的 Fiber 架構&#x…

【JavaSE五天速通|第三篇】常用API與日期類篇

適合有其他語言基礎想快速入門JavaSE的。用的資料是 Java入門基礎視頻教程 &#xff0c;從中摘取了筆者認為與其他語言不同或需要重點學習的內容 常用API與日期類只需要有印象即可&#xff0c;用到了再來這查 day04 常用API 一、StringBuilder類 StringBuilder代表可變字符…

K8s學習筆記(二) Pod入門與實戰

1 K8s核心資源Pod 1.1 Pod是什么&#xff1f; 官方文檔&#xff1a;Pod | Kubernetes Pod 是 Kubernetes&#xff08;k8s&#xff09;中最小的部署與調度單元&#xff0c;并非直接運行容器&#xff0c;而是對一個或多個 “緊密關聯” 容器的封裝。 核心特點可簡單總結為 3 …

用 Python 調用 Bright Data MCP Server:在 VS Code 中實現實時網頁數據抓取

用 Python 調用 Bright Data MCP Server&#xff1a;在 VS Code 中實現實時網頁數據抓取&#xff0c;本文介紹了Bright Data的Web MCP Server&#xff0c;這是一款能實現實時、結構化網頁數據訪問的API&#xff0c;適用于AI應用等場景。其支持靜態與動態網頁&#xff0c;前3個月…

SPSS繪制ROC曲線并計算靈敏度、特異度

SPSS繪制ROC曲線并計算靈敏度、特異度。 &#xff08;1&#xff09;繪制ROC曲線&#xff1a; 輸入&#xff1a;預測值、受試者標簽。 在SPSS中點擊“分析”-“分類”-“ROC曲線” 變量輸入&#xff1a;檢驗變量輸入預測值&#xff0c;狀態變量輸入受試者標簽&#xff0c;如果標…

Modbus協議原理與Go語言實現詳解

目錄 Modbus協議概述協議架構與通信模式Modbus數據模型Modbus協議幀格式功能碼詳解Go Modbus庫完整實現高級應用示例調試與故障排除 Modbus協議概述 Modbus是一種串行通信協議&#xff0c;由Modicon公司&#xff08;現施耐德電氣&#xff09;于1979年開發&#xff0c;用于PL…

下載CentOS 7——從阿里云上下載不同版本的 CentOS 7

沒有廢話&#xff0c;直接上干貨。跟著圖片教程&#xff0c;一步一步來就行。 想下載其它版本的&#xff0c;自己可以再選擇其它的就行。 想省事的朋友可以直接點擊: 1、下載頁面鏈接 2、CentOS-7-x86_64-DVD-2207-02(4.4GB).iso

SpringBoot -原理篇

文章目錄配置優先級Bean管理獲取beanbean作用域第三方beanSpringBoot原理起步依賴自動配置自動配置原理方案源碼跟蹤原理分析 Conditional案例&#xff08;自定義starter&#xff09;案例&#xff08;自定義starter分析&#xff09;案例&#xff08;自定義starter實現&#xff…

JavaScript與jQuery:從入門到面試的完整指南

JavaScript與jQuery&#xff1a;從入門到面試的完整指南 第一部分&#xff1a;JavaScript基礎 1.1 JavaScript簡介 JavaScript是一種輕量級的解釋型編程語言&#xff0c;主要用于Web開發&#xff0c;可以為網頁添加交互功能。它是ECMAScript規范的一種實現。 // 第一個JavaScri…

解決:Ubuntu、Kylin、Rocky系統中root用戶忘記密碼

解決Linux系統中root用戶忘記密碼 Ubuntu2204 重啟電腦&#xff0c;啟動時&#xff0c;長按Shift鍵&#xff08;對于 BIOS 系統&#xff09;或 Esc 鍵&#xff08;對于 UEFI 系統&#xff09;進入GRUB菜單 步驟1&#xff1a;重啟Ubuntu系統&#xff0c;長按Shift鍵進入Ubuntu…

ENVI系列教程(二)——自定義坐標系(北京 54、西安 80、2000 坐標系)

目錄 1 概述 1.1 地理投影的基本原理 1.2 國內坐標系介紹 1.3 參數的獲取 2 詳細操作步驟 2.1 添加橢球體 2.2 添加基準面 2.3 定義坐標系 2.4 使用自定義坐標系 1 概述 1.1 地理投影的基本原理 常用到的地圖坐標系有 2 種,即地理坐標系和投影坐標系。地理坐標系是…

一種基于因果干預的少樣本學習的故障診斷模型

一、研究背景與問題 ?工業背景?:機械故障診斷對工業系統安全至關重要,但實際中故障樣本稀少,難以訓練傳統深度學習模型。 ?現有問題?: 當前少樣本學習(FSL)方法大多基于相關性而非因果關系建模,容易學習到偽相關特征,導致模型可解釋性差、泛化能力弱。 跨組件故障診…

機器視覺光源的尺寸該如何選型的方法

機器視覺光源的尺寸該如何選型的方法&#x1f3af;機器視覺光源的尺寸選型的方法&#x1f3af;一、選型案例&#x1f3af;二、照射方式&#x1f3af;三、鏡頭選擇&#x1f3af;四、光源架構光源的工作距離與視野大小&#x1f3af;五、總結&#xff1a;光源選型 —— 機器視覺檢…

HTML新屬性

HTML5引入了許多新屬性&#xff0c;旨在增強語義化、交互性和多媒體支持。以下是一些重要的新屬性及其用途分類&#xff1a;語義化與結構屬性data-*&#xff1a;自定義數據屬性&#xff0c;允許開發者存儲額外信息&#xff08;如data-id"123"&#xff09;。hidden&am…

從工地到鏈上:一個土建人的 Web3 轉行經歷

Web3 的風&#xff0c;終究還是吹到了土建行業。2017 年&#xff0c;土建專業&#xff08;給排水工程&#xff09;的劉正源偶然看到一則關于比特幣的新聞&#xff0c;被它背后的經濟模型與技術架構深深震撼。到了 2021 年&#xff0c;他在工地上再次聽人提起區塊鏈&#xff0c;…

20250914-03: Langchain概念:提示模板+少樣本提示

20250914-03: Langchain概念&#xff1a;提示模板少樣本提示 聊天模型 消息 提示 結構化輸出 &#x1f3af; 學習目標 掌握如何“喂給模型正確的輸入”并“解析出想要的輸出”。 &#x1f517; 核心概念 ?聊天模型&#xff08;ChatModel&#xff09;?消息&#xff08;M…

【AI推理部署】Docker篇04—Docker自動構建鏡像

Docker 自動構建鏡像1. Dockfile 編寫2. 鏡像使用使用 Dockerfile 構建鏡像 Dockerfile 其實就是把我們前面的一系列安裝、配置命令寫到一個文件中&#xff0c;通過 docker build 命令&#xff0c;一鍵完成鏡像的構建。接下來&#xff0c;我們以 bitnami/pytorch:2.1.1 作為基礎…

LeetCode 674.最長連續遞增序列

給定一個未經排序的整數數組&#xff0c;找到最長且 連續遞增的子序列&#xff0c;并返回該序列的長度。 連續遞增的子序列 可以由兩個下標 l 和 r&#xff08;l < r&#xff09;確定&#xff0c;如果對于每個 l < i < r&#xff0c;都有 nums[i] < nums[i 1] &am…

貪心算法java

貪心算法簡介貪心算法是一種在每一步選擇中都采取在當前狀態下最優&#xff08;局部最優&#xff09;的選擇&#xff0c;從而希望導致結果是全局最優的算法。貪心算法通常用于解決最優化問題&#xff0c;如最短路徑、最小生成樹、任務調度等。貪心算法的基本步驟問題分析&#…