深入实战 —— Protobuf 的序列化与反序列化详解(Go + Java 示例)

在前两篇文章中,我们已经了解了 Protocol Buffers(Protobuf)的基本概念,并深入学习了 .proto 文件的语法结构、消息定义、字段规则等内容。本篇文章将进入实战阶段 ,重点讲解如何使用 Protobuf 进行数据的序列化反序列化操作。

我们将通过完整的示例,演示如何在 Go 和 Java 语言中 使用 Protobuf 完成数据的编码与解码过程,并对比其性能优势,帮助你更好地理解 Protobuf 在实际开发中的应用价值。


一、什么是序列化与反序列化?

1. 序列化(Serialization)

将结构化的数据对象转换为字节流(byte stream),以便在网络上传输或存储到文件中。

2. 反序列化(Deserialization)

将字节流还原为原始的数据对象。

📌 为什么需要序列化?

  • 跨网络传输数据时,必须将数据转为字节形式。
  • 持久化存储结构化数据时,需要统一格式。
  • 不同系统之间交换数据时,需要通用协议。

二、准备工作:编写 .proto 文件

我们先定义一个简单的用户信息模型 user.proto

Go 复制代码
syntax = "proto3";

package user;

message UserInfo {
    string name = 1;
    int32 age = 2;
    string email = 3;
    repeated string roles = 4;
}

然后使用 protoc 编译器生成对应语言的代码:

Go 复制代码
# 生成 Go 代码
protoc --go_out=. user.proto

# 生成 Java 代码
protoc --java_out=. user.proto

三、Go 中的序列化与反序列化

1. 创建并填充对象

Go 复制代码
package main

import (
	"fmt"
	"os"

	pb "./user_go_proto" // 根据你的路径调整
	"github.com/golang/protobuf/proto"
)

func main() {
	user := &pb.UserInfo{
		Name:  "Alice",
		Age:   30,
		Email: "[email protected]",
		Roles: []string{"admin", "developer"},
	}

2. 序列化为字节流

Go 复制代码
	data, err := proto.Marshal(user)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Serialized data (bytes): %v\n", data)

3. 将字节流写入文件

Go 复制代码
	err = os.WriteFile("user.bin", data, 0644)
	if err != nil {
		panic(err)
	}

4. 从字节流恢复对象

Go 复制代码
	newUser := &pb.UserInfo{}
	err = proto.Unmarshal(data, newUser)
	if err != nil {
		panic(err)
	}

	fmt.Println("Name:", newUser.GetName())
	fmt.Println("Age:", newUser.GetAge())
	fmt.Println("Email:", newUser.GetEmail())
	fmt.Println("Roles:", newUser.GetRoles())
}

四、Java 中的序列化与反序列化

1. 创建并填充对象

确保你已导入生成的类 UserInfo

java 复制代码
import user.UserInfo;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        UserInfo user = UserInfo.newBuilder()
                .setName("Bob")
                .setAge(28)
                .setEmail("[email protected]")
                .addRoles("editor")
                .addRoles("member")
                .build();

2. 序列化为字节流

java 复制代码
        byte[] data = user.toByteArray();
        System.out.println("Serialized data (bytes): ");
        for (byte b : data) {
            System.out.printf("%02X ", b);
        }
        System.out.println();

3. 写入文件

Go 复制代码
        try (FileOutputStream output = new FileOutputStream("user_java.bin")) {
            output.write(data);
        }

4. 从字节流恢复对象

java 复制代码
        UserInfo newUser = UserInfo.parseFrom(data);

        System.out.println("Name: " + newUser.getName());
        System.out.println("Age: " + newUser.getAge());
        System.out.println("Email: " + newUser.getEmail());
        System.out.println("Roles: " + newUser.getRolesList());
    }
}

五、Protobuf 序列化的性能优势分析

特性 JSON XML Protobuf
数据大小 较大 很大 极小(通常比 JSON 小 3~5 倍)
编码速度 更快
解码速度 更快
可读性 低(二进制)
向后兼容性

结论

  • Protobuf 适用于对性能敏感带宽受限的场景,如微服务通信、物联网设备、实时数据处理等。
  • 如果你需要人类可读性调试方便,JSON 是更好的选择。

六、常见问题与注意事项

1. 字段编号不能重复

确保每个字段都有唯一的编号,避免因编号冲突导致解析失败。

2. 默认值机制

  • 所有字段如果没有赋值,默认是"零值"(如数字为 0,字符串为空,布尔为 false)。
  • Proto3 不再支持 required,所有字段默认都是可选的。

3. 升级版本需保持兼容性

当你扩展 .proto 文件时,新增字段应使用新的编号,不要修改已有字段类型或编号,否则可能导致旧客户端解析失败。


七、总结

在本文中,我们:

  • 学习了 Protobuf 的核心功能之一:序列化与反序列化
  • 使用 Go 和 Java 实现了完整的编解码流程
  • 对比了 Protobuf 与其他数据格式(如 JSON)的性能优势
  • 掌握了实际开发中需要注意的关键点

通过这些实践,你已经能够熟练地在项目中集成 Protobuf,实现高效的数据交换和跨平台通信。


如果你正在构建高性能服务、微服务架构或分布式系统,Protobuf 是不可或缺的工具。希望这篇文章能帮助你更自信地在项目中使用 Protobuf,并享受它带来的效率提升和开发体验优化。

相关推荐
小道仙9712 分钟前
gitlab对接,gitlabRestApi,gitlab4j-api
java·git·gitlab
AgilityBaby13 分钟前
UE5蓝图按键输入绑定学习笔记
笔记·学习·ue5·蓝图
萌新小码农‍19 分钟前
SpringBoot新闻项目学习day3--后台权限的增删改查以及权限管理分配
spring boot·后端·学习
奈斯ing20 分钟前
【MySQL篇】高效学习官方文档指南(基于MySQL8.0版本详解)
运维·数据库·学习·mysql
oioihoii31 分钟前
C++11 GC Interface:从入门到精通
java·jvm·c++
想用offer打牌44 分钟前
一站式了解责任链模式🥹
后端·设计模式·架构
九转编程蛊1 小时前
破解版idea安装Jira插件
java·ide·intellij-idea
小码编匠1 小时前
面向工业应用的点云相机控制接口库(含C#调用示例)
后端·c#·.net
ggdpzhk1 小时前
输入两个正整数,计算最大公约数和最小公倍数
java·算法
Luffe船长1 小时前
springboot将文件插入到指定路径文件夹,判断文件是否存在以及根据名称删除
java·spring boot·后端·spring