本文為筆者閱讀魚皮的項目 《簡易版 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方法讀取文件中的對象釋放資源
使用讀取出來的對象