鱼皮项目简易版 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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…