魚皮項目簡易版 RPC 框架開發(三)

本文為筆者閱讀魚皮的項目 《簡易版 RPC 框架開發》的筆記,如果有時間可以直接去看原文,

1. 簡易版 RPC 框架開發

前面的內容可以筆者的前面兩個篇筆記

魚皮項目簡易版 RPC 框架開發(一)

魚皮項目簡易版 RPC 框架開發(二)

引用:

1. 簡易版 RPC 框架開發

魚皮項目簡易版 RPC 框架開發(一)

魚皮項目簡易版 RPC 框架開發(二)

RPC框架的簡單理解

ByteArrayOutputStream詳解

Java中ObjectOutputStream和ObjectInputStream的基本使用詳解

ByteArrayInputStream 類詳解

對象的反序列化流ObjectInputStream

在分布式系統中,RPC(遠程過程調用)框架的核心挑戰之一是如何高效地在網絡間傳輸對象數據。序列化器模塊正是解決這一問題的關鍵組件。本文將深入解析一個RPC框架中的序列化器實現,揭示其設計哲學與技術細節。

代碼

Serializer接口

package com.yupi.yurpc.serializer;import java.io.IOException;/*** 序列化器接口*/
public interface Serializer {/*** 序列化** @param object* @param <T>* @return* @throws IOException*/<T> byte[] serialize(T object) throws IOException;/*** 反序列化** @param bytes* @param type* @param <T>* @return* @throws IOException*/<T> T deserialize(byte[] bytes, Class<T> type) throws IOException;
}

JdkSerializer類?

package com.yupi.yurpc.serializer;import java.io.*;/*** JDK 序列化器*/
public class JdkSerializer implements Serializer {/*** 序列化** @param object* @param <T>* @return* @throws IOException*/@Overridepublic <T> byte[] serialize(T object) throws IOException {ByteArrayOutputStream outputStream = new ByteArrayOutputStream();try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream)) {objectOutputStream.writeObject(object);return outputStream.toByteArray();}}/*** 反序列化** @param bytes* @param type* @param <T>* @return* @throws IOException*/@Overridepublic <T> T deserialize(byte[] bytes, Class<T> type) throws IOException {ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);try {return (T) objectInputStream.readObject();} catch (ClassNotFoundException e) {throw new RuntimeException(e);} finally {objectInputStream.close();}}
}

?一、序列化器:分布式通信的基石

序列化器在RPC框架中扮演著數據格式轉換器的角色,主要職責包括:
- 序列化:將內存中的對象轉換為字節流
- 反序列化:將接收的字節流還原為可操作的對象
- 跨語言支持:實現不同語言間的數據交換(可選)
- 性能優化:平衡序列化速度與數據大小

二、抽象接口設計:策略模式的完美實踐

`Serializer.java` 文件定義了序列化器的抽象接口:


public interface Serializer {
? ? <T> byte[] serialize(T object) throws IOException;
? ? <T> T deserialize(byte[] bytes, Class<T> type) throws IOException;
}
?

設計亮點:
1. 泛型支持:使用`<T>`泛型確保類型安全
2. 異常透明:明確聲明`IOException`讓調用方處理異常
3. 簡潔契約:僅定義兩個核心方法,符合接口隔離原則
4. 策略模式:為不同序列化算法提供統一接入點

這種接口設計使得我們可以輕松擴展各種序列化實現(JSON、Protobuf、Hessian等),而無需修改框架核心代碼。

三、JDK實現:Java原生序列化解析

`JdkSerializer.java` 提供了基于Java原生序列化的實現:


// 序列化實現
public <T> byte[] serialize(T object) throws IOException {
? ? ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
? ? try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream)) {
? ? ? ? objectOutputStream.writeObject(object);
? ? ? ? return outputStream.toByteArray();
? ? }
}

// 反序列化實現
public <T> T deserialize(byte[] bytes, Class<T> type) throws IOException {
? ? try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
? ? ? ? ?ObjectInputStream objectInputStream = new ObjectInputStream(inputStream)) {
? ? ? ? return (T) objectInputStream.readObject();
? ? } catch (ClassNotFoundException e) {
? ? ? ? throw new RuntimeException("類未找到: " + e.getMessage());
? ? }
}
?

關鍵技術點:

1. 內存流優化
? ?- 使用`ByteArrayOutputStream`避免磁盤IO
? ?- 字節數組操作大幅提升性能

2. 資源安全管理
? ?- 序列化使用try-with-resources自動關閉資源
? ?- 反序列化優化后同樣采用自動關閉

3. 異常處理策略
? ?- 檢查異常`IOException`傳遞給調用方
? ?- `ClassNotFoundException`轉換為運行時異常
? ?- 添加明確錯誤信息便于問題定位

4. 類型轉換安全
? ?- 基于傳入的`Class<T> type`進行類型校驗
? ?- 類型轉換前確保對象兼容性

四、序列化器設計進階思考

1. 性能優化方向

? ?// 示例:添加緩存優化
? ?private final Map<Class<?>, SoftReference<byte[]>> serializationCache =?
? ? ? ?new ConcurrentHashMap<>();
?

2. 安全增強方案

? ?// 示例:限制反序列化類
? ?objectInputStream.setObjectInputFilter(filterInfo ->?
? ? ? ?allowedClasses.contains(filterInfo.serialClass()) ??
? ? ? ? ? ?ObjectInputFilter.Status.ALLOWED :?
? ? ? ? ? ?ObjectInputFilter.Status.REJECTED);
?

3. 擴展性設計

? ?// 示例:支持多種序列化協議
? ?public enum SerializerAlgorithm {
? ? ? ?JDK((byte)1), JSON((byte)2), PROTOBUF((byte)3);
? ? ? ?
? ? ? ?public static Serializer getByCode(byte code) {
? ? ? ? ? ?// 根據編碼返回對應序列化器
? ? ? ?}
? ?}
?

?五、序列化技術選型對比

| 特性 ? ? ? ? ?| JDK序列化 ? ?| JSON ? ? ? | Protobuf ? |
|---------------|------------|------------|------------|
| 跨語言支持 ? ? ?| ? ? ? ? ? ?| ? ? ? ? ? ?| ? ? ? ? ? ?|
| 數據大小 ? ? ? ?| 大 ? ? ? ? ?| 中 ? ? ? ? ?| 小 ? ? ? ? ?|
| 性能 ? ? ? ? ? | 低 ? ? ? ? ?| 中 ? ? ? ? ?| 高 ? ? ? ? ?|
| 可讀性 ? ? ? ? | ? ? ? ? ? ?| ? ? ? ? ? ?| ? ? ? ? ? ?|
| 開發便利性 ? ? ?| ? ? ? ? ? ?| ? ? ? ? ? ?| 中 ? ? ? ? ?|

選型建議:內部系統可優先考慮JDK序列化;跨語言場景推薦Protobuf;調試階段可使用JSON

?六、最佳實踐總結

1. 接口隔離原則:保持序列化接口簡潔明確
2. 資源安全:始終確保流的正確關閉
3. 類型安全:在反序列化時驗證類型信息
4. 異常分層:區分可恢復異常與系統級錯誤
5. 可擴展設計:預留協議升級和算法替換能力
6. 安全防護:對反序列化操作施加白名單限制

序列化器作為RPC框架的通信基石,其設計質量直接影響整個系統的性能和可靠性。通過清晰的接口定義和嚴謹的實現細節,我們為構建高性能分布式系統奠定了堅實基礎。

補充

ByteArrayOutputStream

ByteArrayOutputStream 對byte類型數據進行寫入的類 相當于一個中間緩沖層,將類寫入到文件等其他outputStream。它是對字節進行操作,屬于內存操作流

ByteArrayOutputStream繼承了OutputStream類

ByteArrayOutputStream類中的成員和方法的介紹:

protected byte buf[];
//數據存儲的地方
protected int count;
//計數器  表示數據的個數

ByteArrayOutputStream的構造方法有兩個;

//創建一個新的 byte 數組輸出流。緩沖區的容量最初是 32 字節,如有必要可增加其大小public ByteArrayOutputStream() {this(32);}//創建一個新的 byte 數組輸出流,它具有指定大小的緩沖區容量(以字節為單位)public ByteArrayOutputStream(int size) {if (size < 0) {throw new IllegalArgumentException("Negative initial size: "+ size);}buf = new byte[size];}

而ByteArrayOutputStream中有三個write()方法:

//將指定的int類型的數據寫入此 byte 數組輸出流
public ?void write(int b){ensureCapacity(count + 1);buf[count] = (byte) b;count += 1;
}/**將指定 byte 數組中從偏移量?off 開始的?len 個字節寫入此 byte 數組輸出流。*/
public ?void write(byte b[], int off, int len){if ((off < 0) || (off > b.length) || (len < 0) ||((off + len) - b.length > 0)) {throw new IndexOutOfBoundsException();}ensureCapacity(count + len);System.arraycopy(b, off, buf, count, len);count += len;
}

toByteArray()方法

//創建一個新分配的 byte 數組。其大小是此輸出流的當前大小,并且緩沖區的有效內容已復制到該數組中。public synchronized byte toByteArray()[] {return Arrays.copyOf(buf, count);}

ObjectOutputStream


ObjectOutputStream是一個高級流, 將 Java 對象的基本數據類型和圖形寫入 OutputStream。可以使用 ObjectInputStream 讀取(重構)對象。通過在流中使用文件可以實現對象的持久存儲。如果流是網絡套接字流,則可以在另一臺主機上或另一個進程中重構對象。

注意:只能將支持 java.io.Serializable 接口的對象寫入流中。每個 serializable 對象的類都被編碼,編碼內容包括類名和類簽名、對象的字段值和數組值,以及從初始對象中引用的其他所有對象的閉包。

構造函數
//為完全重新實現 ObjectOutputStream 的子類提供一種方法,讓它不必分配僅由 ObjectOutputStream 的實現使用的私有數據。
protected ObjectOutputStream();//創建寫入指定 OutputStream 的 ObjectOutputStream。此構造方法將序列化流部分寫入底層流;調用者可以通過立即刷新流,確保在讀取頭部時,用于接收 ObjectInputStreams 構造方法不會阻塞。
public ObjectOutputStream(OutputStream out);

常用方法

//將指定的對象寫入 ObjectOutputStream。對象的類、類的簽名,以及類及其所有超類型的非瞬態和非靜態字段的值都將被寫入。

public final void writeObject(Object obj);

這兩個的input對應的是他們的輸入方法

ByteArrayInputStream 類詳解

ByteArrayInputStream?是 Java 中用于從字節數組讀取數據的輸入流,位于?java.io?包。它允許將內存中的字節數組當作輸入流來讀取,是處理內存數據的常用工具。

1. 核心特性
內存數據源:從字節數組(byte[])讀取數據
無需關閉:close() 方法為空操作(無系統資源需要釋放)
線程不安全:多線程訪問需外部同步
支持標記/重置:可重復讀取數據(mark() 和 reset())
2. 類繼承關系

3. 構造方法
? ? ? ? ? ? ? ?構造方法? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 說明
ByteArrayInputStream(byte[] buf)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 使用整個字節數組作為數據源
ByteArrayInputStream(byte[] buf, int offset, int length)?? ?使用數組的指定區間
4. 核心方法
(1)讀取數據
? ? ? ? ? ? 方法? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?說明
int read()? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 讀取單個字節(返回0-255,-1表示結束)
int read(byte[] b, int off, int len)?? ?讀取數據到字節數組
long skip(long n)? ? ? ? ? ? ? ? ? ? ? ? ? 跳過指定字節數

ObjectInputStream

該流位于API中java,io.ObjectInputStream,作用是將文件中的對象,反序列化為,以流的方式讀取出來

ObjectInputStream中的構造方法


ObjectInputStream(InputStream in)創建從指定 InputStream 讀取的 ObjectInputStream

ObjectInputStream中特有的成員方法


Object readObject()從 ObjectInputStream 讀取對象

ObjectInputStream的使用步驟


創建ObjectInputStream對象,構造方法中傳遞字節輸入流


使用ObjectInputStream對象中的readObject方法讀取文件中的對象

釋放資源


使用讀取出來的對象

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

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

相關文章

嵌入式Linux:注冊線程清理處理函數

在 Linux 多線程編程中&#xff0c;線程終止時可以執行特定的清理操作&#xff0c;通過注冊線程清理函數&#xff08;thread cleanup handler&#xff09;來實現。這類似于使用 atexit() 注冊進程終止處理函數。線程清理函數用于在線程退出時執行一些資源釋放或清理工作&#x…

【Git】Linux-ubuntu 22.04 初步認識 -> 安裝 -> 基礎操作

文章目錄Git 初識Git 安裝Linux-centosLinux-ubuntuWindowsGit 基本操作配置 Git認識工作區、暫存區、版本庫添加文件 -- 場景一查看 .git 文件添加文件 -- 場景二修改文件版本回退撤銷修改情況一&#xff1a;對于工作區的代碼&#xff0c;還沒有 add情況二&#xff1a;已經 ad…

輕量級音樂元數據編輯器Metadata Remote

簡介 什么是 Metadata Remote (mdrm) &#xff1f; Metadata Remote 是一個基于 Web 的音頻元數據編輯工具&#xff0c;旨在簡化在無頭服務器&#xff08;即沒有圖形用戶界面的服務器&#xff09;上編輯音頻文件的元數據。用戶只需使用 Docker 和瀏覽器&#xff0c;無需復雜的…

免費使用|共享服務器上線RTX3080(20GB顯存)

共享服務器也上架GPU啦 生物信息學中有很多用到GPU的場景&#xff0c;例如我們分享過的&#xff1a;利用GPU加速TensorFlow、部署本地DeepSeek&#xff0c;空間轉錄組學習手冊合輯加速。因此多種GPU供大家選擇&#xff1a;RTX5090、4080S、5070顯卡上機。為了讓此前的CPU服務器…

搭建DM數據守護集群

1環境與規劃準備3個kylin 10操作系統的虛擬機&#xff0c;規劃IP、端口、安裝目錄等。說明搭建REALTIME歸檔模式、事務一致性的數據守護名稱項初始主庫機器dm1初始備庫機器dm2監視器機器dmmon外部業務IP192.168.23.129192.168.23.130192.168.23.131內部心跳IP192.168.23.129192…

AUTOSAR進階圖解==>AUTOSAR_SRS_OCUDriver

AUTOSAR OCU驅動程序詳解 AUTOSAR標準輸出比較單元驅動程序架構與實現分析目錄 1. 概述 1.1 OCU驅動程序簡介1.2 功能概述 2. OCU驅動程序架構 2.1 架構圖2.2 層次結構 3. OCU驅動程序組件設計 3.1 組件圖3.2 接口定義 4. OCU驅動程序狀態管理 4.1 狀態圖4.2 狀態轉換 5. OCU驅…

InfluxDB 與 HTTP 協議交互進階(一)

引言 在當今數字化時代&#xff0c;數據處理的高效性和準確性成為了眾多領域關注的焦點。InfluxDB 作為一款開源的時序數據庫&#xff0c;憑借其高性能、易擴展等特性&#xff0c;在時間序列數據處理中占據了重要地位。而 HTTP 協議作為互聯網應用層的核心協議之一&#xff0c…

NAS遠程訪問新解法:OMV與cpolar的技術協同價值

文章目錄前言1. OMV安裝Cpolar2. 配置FTP公網地址3. OMV FTP 配置4. OMV FTP遠程連接前言 當家庭存儲需求突破本地邊界時&#xff0c;傳統NAS方案往往陷入"連接困境"&#xff1a;復雜的端口轉發配置、高昂的公網IP成本、以及始終存在的安全顧慮…開源解決方案OMV雖然…

vue 渲染 | 不同類型的元素渲染的方式(vue組件/htmlelement/純 html)

省流總結&#xff1a;&#xff08;具體實現見下方&#xff09; vue 組件 ——》<component :is組件名> htmlelement 元素 ——》 ref 、★ v-for ref 或是 ★ vue 的 nextTick 純 html 結構——》v-html 另外&#xff0c;當數據異步加載時&#xff0c;vue3中如何渲…

Charles中文版深度解析,輕松調試API與優化網絡請求

在現代軟件開發過程中&#xff0c;調試API、捕獲HTTP/HTTPS流量以及優化網絡性能是開發者不可避免的挑戰。特別是在處理復雜的網絡請求和驗證API接口的數據傳輸準確性時&#xff0c;開發者需要一款強大且易于使用的工具。Charles抓包工具憑借其功能強大、界面簡潔、易于操作的特…

【CF】Codeforces Round 1039 (Div. 2) E1 (二分答案求中位數)

E1. Submedians (Easy Version)題目&#xff1a;思路&#xff1a;經典不過加了點東西對于求中位數&#xff0c;我們必然要想到二分答案&#xff0c;具體的&#xff0c;對于所有大于等于 x 的數我們令其奉獻為 1&#xff0c;小于的為 -1&#xff0c;如果存在某段區間的奉獻和大于…

ESP32-S3學習筆記<8>:LEDC的應用

ESP32-S3學習筆記&#xff1c;8&#xff1e;&#xff1a;LEDC的應用1. 頭文件包含2. LEDC的配置2.1 配置定時器2.1.1 speed_mode/設置速度模式2.1.2 duty_resolution/設置占空比分辨率2.1.3 timer_num/選擇定時器2.1.4 freq_hz/設定PWM頻率2.1.5 clk_cfg/選擇LEDC的外設時鐘源2…

網絡安全第14集

前言&#xff1a;小迪安全14集&#xff0c;這集重點內容&#xff1a;0、什么是js滲透測試&#xff1f;在javascript中也存在變量和函數&#xff0c;存在可控變量和函數就有可能存在在漏洞&#xff0c;js開發的web應用和php、java開發的區別是&#xff0c;js能看得到的源代碼&am…

代碼隨想錄算法訓練營第三十三天

LeetCode.62 不同路徑 題目鏈接 不同路徑 題解 class Solution {public int uniquePaths(int m, int n) {// dp表示到達ij有多少條路徑int[][] dp new int[110][110];dp[1][1] 1;for(int i 0;i<m;i){dp[i][0] 1;}for(int j 0;j<n;j){dp[0][j] 1;}for(int i 1;i…

銀行回單OCR識別技術原理

銀行回單OCR&#xff08;光學字符識別&#xff09;技術通過結合圖像處理、模式識別和自然語言處理&#xff08;NLP&#xff09;技術&#xff0c;將紙質或電子版銀行回單中的非結構化文本&#xff08;如賬號、金額、日期等&#xff09;轉化為結構化數據。以下是其核心原理和關鍵…

Day22-二叉樹的迭代遍歷

昨天學習了遞歸遍歷&#xff1a;遞歸就是一次次的把參數壓入棧中&#xff0c;然后返回的時候還是上一次遞歸保存的參數。今天學習迭代遍歷。迭代遍歷就是用棧去模擬保存二叉樹的節點&#xff0c;然后依次去遍歷&#xff0c;只不過要注意棧的后入先出的規則。前序遍歷&#xff1…

知識蒸餾 - 通過引入溫度參數T調整 Softmax 的輸出

知識蒸餾 - 通過引入溫度參數T調整 Softmax 的輸出 flyfish import torch import torch.nn.functional as F import matplotlib.pyplot as plt import numpy as np# 設置中文字體支持 plt.rcParams["font.family"] [AR PL UMing CN] # Linux plt.rcParams[axes.uni…

Java研學-RabbitMQ(三)

一 消息通信協議 1 AMQP AMQP 是一個開放的、跨語言、跨平臺的消息協議標準&#xff0c;用于在分布式系統中傳遞業務消息。它定義了消息隊列的二進制協議格式和交互模型&#xff08;如交換機、隊列、綁定等&#xff09;&#xff0c;確保不同語言&#xff08;Java、Python、C#等…

http.client 教程-如何使用 Python 標準庫發送 HTTP 請求

http.client 教程-如何使用 Python 標準庫發送 HTTP 請求以下是 http.client 模塊的詳細使用教程&#xff0c;幫助你理解如何使用 Python 標準庫發送 HTTP 請求&#xff1a;1. http.client 概述http.client 是 Python 內置的 HTTP 客戶端庫&#xff0c;提供了底層的 HTTP 協議實…

Android-三種持久化方式詳解

持久化技術分為3種&#xff0c;文件&#xff0c;sharedPreferences存儲&#xff0c;數據庫來存儲&#xff1b; 目錄 文件存儲&#xff1a; 利用SharedPreferences中讀取數據 SQLite創建數據庫 更新 添加 刪除 查找&#xff1a; 文件存儲&#xff1a; 文件存儲是 Andr…