Netty
1、概述
2、线程模型
3、核心组件
3.1 Channel
3.2 ChannelHandler和ChannelPipeline
3.3 EventLoop和EventLoopGroup
3.4 Future和Promise
4、创建过程
4.1 服务端创建过程
4.2 客户端创建过程
5、TCP粘包和拆包
6、序列化与反序列化
-
+
游客
注册
登录
序列化与反序列化
## 1 为什么要进行序列化与反序列化 1. **序列化是指将数据结构**(如 C++ 中的 struct 就是数据结构类型)**或对象**(如 Java 中实例化后的类)**转换成二进制字节流的过程**,**反序列化是指把二进制字节流转换成数据结构或者对象的过程**。 2. **要将数据进行网络传输就要涉及到序列化**,因为**网络传输的方式就是 IO**,而**我们的 IO 支持数据格式就是字节数组**,但是我们单方面**只把对象转成字节数组还不行**,因为**没有规则的字节数组我们没办法把对象的本来面目还原回来**,所以**我们必须在把对象转成字节数组的时候就制定一种规则**(序列化),那么**我们从 IO 流里面读出数据的时候再以这种规则把对象还原回来**(反序列化)。 3. 序列化的主要目的是**通过网络传输对象**或者说是**将对象存储到文件系统**、**数据库**、**内存中**,因此,实际开发中主要有以下场景会用到序列化和反序列化: 1. 对象在**进行网络传输**(比如远程方法调用 RPC)**之前需要先被序列化**,**接收到序列化的对象之后需要再进行反序列化**。 2. 将对象**存储到文件中的时候需要进行序列化**,**将对象从文件中读取出来需要进行反序列化**。 3. 将对象**存储到缓存数据库**(如 Redis)**时需要用到序列化**,**将对象从缓存数据库中读取出来需要反序列化**。 ## 2 常见的序列化协议 常用的序列化协议有**Kyro**、**Protobuf**、**ProtoStuff**、**Hession**(JDK 自带的序列化方式一般不用,因为序列化效率低并且部分版本有安全漏洞,JSON、XML 这种属于文本类序列化方式,虽然可读性较好,但是性能较差,一般也不会选择)。 ### 2.1 JDK 自带的序列化方式 1. JDK 自带的序列化,**只需实现 `java.io.Serializable` 接口即可**: ```java @AllArgsConstructor @NoArgsConstructor @Getter @Builder @ToString public class RpcRequest implements Serializable { private static final long serialVersionUID = 1905122041950251207L; private String requestId; private String interfaceName; private String methodName; private Object[] parameters; private Class<?>[] paramTypes; private RpcMessageTypeEnum rpcMessageTypeEnum; } ``` 2. **序列化号 `serialVersionUID` 属于版本控制的作用**,**序列化的时候也会被写入二进制序列中**,当**反序列化时会检查 `serialVersionUID` 是否和当前类的 `serialVersionUID` 一致**,**如果不一致**,则**会抛出 `InvalidClassException` 异常**,**强烈推荐每个序列化类都手动指定其 `serialVersionUID`**,**如果不手动指定**,那么**编译器会动态生成默认的序列化号**。 3. 我们很少或者说几乎不会直接使用这个序列化方式,主要原因为: 1. **不支持跨语言调用**: 1. **如果调用的是其他语言开发的服务的时候就不支持了**。 2. **性能差**: 1. 相比于其他序列化框架**性能更低**,主要原因是**序列化之后的字节数组体积较大**,**导致传输成本加大**。 ### 2.2 Kyro 1. Kyro 是一个**高性能的序列化/反序列化工具**,由于其**变长存储**特性并**使用了字节码生成机制**,**拥有较高的运行速度和较小的字节码体积**。 2. 另外,Kyro 是一种**非常成熟的序列化实现**了,已经在 Twitter、Groupon、Yahoo 以及多个著名开源项目(如 Hive、Storm)中广泛的使用。 3. 具体的序列化和反序列化的代码如下: ```java /** * Kryo 序列化类,Kryo 序列化效率很高,但是只兼容 Java 语言 * * @author shuang.kou * @createTime 2020 年 05 月 13 日 19:29:00 */ @Slf4j public class KryoSerializer implements Serializer { /** * 由于 Kryo 不是线程安全的。每个线程都应该有自己的 Kryo,Input 和 Output 实例。 * 所以,使用 ThreadLocal 存放 Kryo 对象 */ private final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> { Kryo kryo = new Kryo(); kryo.register(RpcResponse.class); kryo.register(RpcRequest.class); kryo.setReferences(true); //默认值为 true,是否关闭注册行为,关闭之后可能存在序列化问题,一般推荐设置为 true kryo.setRegistrationRequired(false); //默认值为 false,是否关闭循环引用,可以提高性能,但是一般不推荐设置为 true return kryo; }); @Override public byte[] serialize(Object obj) { try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); Output output = new Output(byteArrayOutputStream)) { Kryo kryo = kryoThreadLocal.get(); // Object->byte:将对象序列化为 byte 数组 kryo.writeObject(output, obj); kryoThreadLocal.remove(); return output.toBytes(); } catch (Exception e) { throw new SerializeException("序列化失败"); } } @Override public <T> T deserialize(byte[] bytes, Class<T> clazz) { try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); Input input = new Input(byteArrayInputStream)) { Kryo kryo = kryoThreadLocal.get(); // byte->Object:从 byte 数组中反序列化出对对象 Object o = kryo.readObject(input, clazz); kryoThreadLocal.remove(); return clazz.cast(o); } catch (Exception e) { throw new SerializeException("反序列化失败"); } } } ``` ### 2.3 Protobuf 1. Protobuf 出自于 Google,**性能还比较优秀**,也**支持多种语言**,同时还是**跨平台**的。 2. Protobuf**包含序列化格式的定义**、**各种语言的库以及一个 IDL 编译器**,正常情况下**我们需要定义 `proto` 文件**,然后**使用 IDL 编译器编译成我们需要的语言**。 3. Protobuf**在使用中比较繁琐**,**需要我们自己定义 IDL 文件和生成对应的序列化代码**,这样**虽然不灵活**,但是,另一方面导致 Protobuf**没有序列化漏洞的风险**。 4. 一个简单的 `proto` 文件如下: ```java // protobuf 的版本 syntax = "proto3"; // SearchRequest 会被编译成不同的编程语言的相应对象,比如 Java 中的 class、Go 中的 struct message Person { //string 类型字段 string name = 1; // int 类型字段 int32 age = 2; } ``` ### 2.4 ProtoStuff 1. ProtoStuff**基于 Google Protobuf**,但是**提供了更多的功能和更简易的用法**,虽然更加易用,但是并不代表 ProtoStuff 性能更差。 ### 2.5 Hession 1. Hession 是一个**轻量级**的、**自定义描述**的二进制 RPC 协议,是一个**比较老的序列化的实现**,同样也是**跨语言**的。 2. Dubbo RPC**默认启用的序列化方式是 Hession2**,但是,Dubbo 对 Hession2 进行了修改,不过大体结构还是差不多的。 ## 参考文献 1. [03 如何自己实现一个 RPC 框架?](https://www.yuque.com/books/share/b7a2512c-6f7a-4afe-9d7e-5936b4c4cab0/hc6nzg) 2. [PROTOSTUFF 序列化分析](https://www.cnblogs.com/zhangboyu/p/7448893.html)。
ricear
2021年9月1日 14:43
©
BY-NC-ND(4.0)
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码