序列化协议详解
序列化协议详解
Section titled “序列化协议详解”什么是序列化?
Section titled “什么是序列化?”对象(内存) ── 序列化 ──> 字节数组(磁盘/网络)字节数组(磁盘/网络)── 反序列化 ──> 对象(内存)序列化在 RPC 中的位置
Section titled “序列化在 RPC 中的位置”Client Server │ │ │ 序列化 Request │ │ ─────────────────────────> │ │ │ 反序列化 Request │ │ ──────┐ │ │ │ │ │ 执行业务 │ │ │ │ │ <─────┘ │ │ 序列化 Response │ <───────────────────────── │ │ 反序列化 Response │常见序列化协议
Section titled “常见序列化协议”1. Java 原生序列化
Section titled “1. Java 原生序列化”// 序列化User user = new User(1, "张三");ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.txt"));out.writeObject(user);
// 反序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.txt"));User user = (User) in.readObject();缺点:
- 性能差(反射机制)
- 安全问题(可执行任意类)
- 跨语言困难
- 序列化后体积大
2. Hessian
Section titled “2. Hessian”// 序列化Hessian2Output out = new Hessian2Output(byteArrayOutputStream);out.writeObject(user);
// 反序列化Hessian2Input in = new Hessian2Input(new ByteArrayInputStream(bytes));User user = (User) in.readObject();优点:
- 性能好(自定义序列化)
- 跨语言支持
- 序列化体积小
缺点:
- 不支持循环引用
- 部分类型支持有限
3. Kryo
Section titled “3. Kryo”// 序列化Kryo kryo = new Kryo();Output output = new Output(new FileOutputStream("user.txt"));kryo.writeObject(output, user);
// 反序列化Input input = new Input(new FileInputStream("user.txt"));User user = kryo.readObject(input, User.class);优点:
- 序列化速度极快
- 序列化体积小
- 支持循环引用
缺点:
- 跨语言支持差
- 线程不安全(需要 ThreadLocal)
4. Protobuf
Section titled “4. Protobuf”// 定义 proto 文件message User { int32 id = 1; string name = 2; string email = 3;}
// 生成 Java 代码// protoc --java_out=. user.proto// 序列化UserProto.User user = UserProto.User.newBuilder() .setId(1) .setName("张三") .setEmail("zhangsan@example.com") .build();byte[] bytes = user.toByteArray();
// 反序列化UserProto.User user = UserProto.User.parseFrom(bytes);优点:
- 序列化/反序列化速度极快
- 序列化体积最小
- 跨语言支持好
- 向后兼容性好
缺点:
- 需要预先定义 IDL 文件
- 学习成本高
5. JSON
Section titled “5. JSON”// 序列化ObjectMapper mapper = new ObjectMapper();String json = mapper.writeValueAsString(user);
// 反序列化User user = mapper.readValue(json, User.class);优点:
- 可读性好
- 跨语言天然支持
- HTTP 友好
缺点:
- 序列化体积大
- 序列化/反序列化速度慢
- 不支持循环引用
| 序列化方式 | 序列化速度 | 反序列化速度 | 体积 | 跨语言 |
|---|---|---|---|---|
| Java 原生 | 1x | 1x | 1x | ❌ |
| Hessian | 3x | 3x | 0.5x | ✅ |
| Kryo | 10x | 10x | 0.3x | ❌ |
| Protobuf | 15x | 15x | 0.2x | ✅ |
| JSON | 2x | 2x | 2x | ✅ |
注:以上为大致对比,实际性能受数据结构和 JVM 影响
序列化选型建议
Section titled “序列化选型建议”┌─────────────────────────────────────────────────────────────┐│ 序列化选型决策树 │└─────────────────────────────────────────────────────────────┘
是否需要跨语言? │ ├─ 是 ──> 是否对性能要求极高? │ │ │ ├─ 是 ──> Protobuf │ │ │ └─ 否 ──> JSON │ └─ 否 ──> 是否对性能要求极高? │ ├─ 是 ──> Kryo / Protobuf │ └─ 否 ──> Java 原生 / Hessian| 场景 | 推荐序列化 |
|---|---|
| 内部服务调用(高性能) | Kryo |
| 跨语言高性能调用 | Protobuf |
| 对外 API | JSON |
| 内部 Java 服务 | Hessian / Kryo |
| 大数据量传输 | Protobuf |
面试高频问题
Section titled “面试高频问题”Q1: 为什么 Java 原生序列化不推荐使用?
Section titled “Q1: 为什么 Java 原生序列化不推荐使用?”参考答案:
- 性能差:使用反射机制,开销大
- 安全问题:可以反序列化任意类,存在安全漏洞
- 序列化体积大:包含大量元数据
- 跨语言困难:其他语言难以解析
Q2: Kryo 序列化有什么优缺点?
Section titled “Q2: Kryo 序列化有什么优缺点?”参考答案:
- 优点:序列化速度快,体积小,支持循环引用
- 缺点:跨语言支持差,线程不安全(需使用 ThreadLocal)
Q3: Protobuf 为什么性能高?
Section titled “Q3: Protobuf 为什么性能高?”参考答案:
- 采用二进制格式,存储紧凑
- 编码/解码只需要简单的位移操作
- 预先定义 IDL,不需要额外的类型信息
- 支持向前/向后兼容(Tag-Wire 格式)