ProtoBuf:从序列化原理到高性能架构底座(一)

专栏:ProtoBuf 高效修炼手册

个人主页:手握风云

目录

[一、初始 ProtoBuf](#一、初始 ProtoBuf)

[1.1. 序列化与反序列化](#1.1. 序列化与反序列化)

[1. 核心定义](#1. 核心定义)

[2. 使用场景](#2. 使用场景)

[1.2. ProtoBuf 基础介绍](#1.2. ProtoBuf 基础介绍)

[1.3. 使用特点与完整工作流程](#1.3. 使用特点与完整工作流程)

[二、ProtoBuf 安装](#二、ProtoBuf 安装)

[2.1. Windows 环境安装](#2.1. Windows 环境安装)

[2.2. CentOS 环境安装](#2.2. CentOS 环境安装)

[三、ProtoBuf 快速上手](#三、ProtoBuf 快速上手)

[3.1. 引入 Maven 依赖](#3.1. 引入 Maven 依赖)

[3.2. 编写 .proto 协议文件](#3.2. 编写 .proto 协议文件)

[1. 目录与文件命名规范](#1. 目录与文件命名规范)

[2. .proto 文件基础语法配置](#2. .proto 文件基础语法配置)

[3. 定义 Message](#3. 定义 Message)

[4. 定义消息字段](#4. 定义消息字段)

[3.3. 编译 .proto 文件](#3.3. 编译 .proto 文件)

[1. 命令行编译](#1. 命令行编译)

[2. Maven 插件编译](#2. Maven 插件编译)

[3.4. 生成的 Java 类核心结构](#3.4. 生成的 Java 类核心结构)

[3.5. 实现序列化与反序列化](#3.5. 实现序列化与反序列化)


一、初始 ProtoBuf

1.1. 序列化与反序列化

1. 核心定义

  • 序列化:把内存中的对象转换为字节序列的过程。
  • 反序列化:把字节序列还原为原始对象的过程,是序列化的逆向操作。

2. 使用场景

数据持久化存储:将内存中的对象状态保存到本地文件、数据库中。网络数据传输:网络无法直接传输编程语言对象,需先将对象序列化为字节流发送;接收端再通过反序列化还原为对象(典型场景:Socket 网络编程)。

1.2. ProtoBuf 基础介绍

支持 Java、C++、Python 等主流编程语言,可在不同操作系统、运行平台上使用。相较于 XML,序列化后数据体积更小、编解码速度更快、语法与使用逻辑更简单。还具有高扩展性与兼容性,后续迭代、修改数据结构时,不会影响线上正在运行的、基于旧数据结构的程序。

1.3. 使用特点与完整工作流程

ProtoBuf 并非直接手写序列化逻辑,而是基于协议文件 + 编译器自动生成代码的模式工作,整体流程固定。

ProtoBuf 采用协议文件结合编译器自动生成代码的工作模式,使用时首先由开发者编写 .proto 协议文件来定义数据结构,再借助 protoc 编译器对协议文件进行编译,工具会自动生成对应编程语言的实体类以及配套工具方法,其中涵盖了序列化、反序列化、字段读写等各类接口,最后业务代码直接依赖编译生成的代码开展开发工作,无需手动编写协议解析逻辑,就能完成对象操作与数据编解码。

它的标准使用流程分为三个环节:第一步是编写 .proto 文件,该文件用于自定义结构化对象及其属性字段,相当于约定好数据交互与存储所使用的协议格式;第二步是使用 protoc 编译器编译 .proto 文件,解析协议内容并自动生成 Java 等目标语言代码;第三步是在业务代码中调用已生成的代码,完成字段的赋值与读取,同时实现对象与字节序列之间的序列化和反序列化操作。

二、ProtoBuf 安装

2.1. Windows 环境安装

官方下载地址:https://github.com/protocolbuffers/protobuf/releases。无需强制下载最新版。

将解压目录中的 bin 文件夹路径,添加到系统 / 用户环境变量的 Path 列表中。然后在 cmd 终端里面输入 protoc --version,检验是否安装成功。

2.2. CentOS 环境安装

bash 复制代码
sudo yum install autoconf automake libtool curl make gcc-c++ unzip

我们还是在之前的 Github 仓库中下载同样的 Protocol Buffers 版本。

bash 复制代码
# 下载命令
wget https://github.com/protocolbuffers/protobuf/releases/download/v21.11/protobuf-all-21.11.zip
bash 复制代码
# 解压并进入目录
unzip protobuf-all-21.11.zip
cd protobuf-21.11
bash 复制代码
# 执行 configure,选择安装路径
./configure

编译、测试、正式安装,单步执行时间约 15 分钟。

bash 复制代码
make          # 源码编译
make check    # 单元测试校验
sudo make install  # 正式安装

最后还是输入 protoc --version 检验是否安装成功。

三、ProtoBuf 快速上手

3.1. 引入 Maven 依赖

XML 复制代码
<!-- Source: https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
<dependency>
	<groupId>com.google.protobuf</groupId>
	<artifactId>protobuf-java</artifactId>
	<version>${protobuf_version}</version>
	<scope>compile</scope>
</dependency>

使用 ProtoBuf 首先需要在 pom.xml 引入官方 Java 客户端依赖,依赖版本必须与本地 protoc 编译器版本保持一致。

3.2. 编写 .proto 协议文件

.proto 是 ProtoBuf 的协议定义文件,用于描述结构化数据(消息、字段、类型),是整个使用流程的入口。

1. 目录与文件命名规范

所有 .proto 文件统一存放于项目 src/main/proto 目录下;全小写字母,多个单词用下划线分隔,如 contacts;单行注释://,多行注释:/**/。

2. .proto 文件基础语法配置

必须在注释之外的第一行声明语法版本,这里使用主流的 proto3。如果为声明,则编译器默认使用 proto2。

复制代码
syntax = "proto3";

package 命名空间(可选):用于区分不同协议、避免消息类名冲突,建议配置。

复制代码
package start;

Java 专属 Option 配置:

配置项 作用
java_multiple_files true:每个 message 生成独立 Java 文件;false:所有内容合并到一个类中
java_package 强制指定编译后 Java 类的包名,优先级高于 package 关键字
java_outer_classname 定义协议总入口包装类名称
java 复制代码
option java_multiple_files = true;
option java_package = "com.example.start";
option java_outer_classname = "ContactProtos";

3. 定义 Message

message 对应 Java 中的实体对象,用来描述业务数据结构。命名规范使用大驼峰命名。

java 复制代码
message 消息名 {
    // 字段定义
}

4. 定义消息字段

字段基础格式:字段类型 字段名 = 字段唯一编号; 字段名采用全小写 + 下划线的格式。字段唯一编号是核心标识,一旦上线禁止修改。

java 复制代码
// 定义联系人
message PeopleInfo {
  // 字段类型 字段名 = 字段唯一编号
  string name = 1;
  int32 age = 2;
}
.proto 类型 说明 对应 Java 类型
string 字符串(UTF-8/ASCII) String
int32 变长编码整型,负数效率低 int
bool 布尔类型 boolean
float/double 浮点型 float/double
bytes 任意字节序列 ByteString

字段编号强制规则 字段编号的合法取值范围为 1 ~ 536,870,911,其中 19000 ~ 19999 是 Protobuf 内部预留区间,开发者不能使用该区间内的编号,否则编译过程中会出现告警。从编码效率角度出发也有对应的使用建议,编号 1 至 15 仅需 1 字节完成编码,适合分配给项目里高频使用的字段;编号 16 至 2047 需要占用 2 字节编码,更适合分配给使用频次较低的字段。

3.3. 编译 .proto 文件

编译的核心作用:通过 protoc 编译器,将 .proto 协议文件自动生成 Java 实体类,生成的类内置序列化、反序列化、字段读写等方法。

1. 命令行编译

命令格式:

bash 复制代码
protoc [--proto_path=协议文件目录] --java_out=Java输出目录 协议文件路径
bash 复制代码
protoc -I src/main/proto/start --java_out=src/main/java contacts.proto

2. Maven 插件编译

XML 复制代码
<plugin>
    <groupId>org.xolstice.maven.plugins</groupId>
    <artifactId>protobuf-maven-plugin</artifactId>
    <version>0.6.1</version>
    <configuration>
        <!-- 本地 protoc.exe 编译器绝对路径(必须配置) -->
        <protocExecutable>C:\xxx\protoc-21.11-win64\bin\protoc.exe</protocExecutable>
        <!-- .proto 文件根目录 -->
        <protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
        <!-- 生成 Java 文件的输出目录 -->
        <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
        <!-- 禁止清空输出目录,防止误删项目代码 -->
        <clearOutputDirectory>false</clearOutputDirectory>
    </configuration>
</plugin>

在 Maven 工具栏执行 protobuf:compile 指令,自动完成编译。

因为我们之前配置了 java_multiple_files = true,编译后生成3 个独立 Java 文件:

  1. ContactsProtos.java:协议总包装类;
  2. PeopleInfo.java:联系人消息对应的实体主类(核心使用类);
  3. PeopleInfoOrBuilder.java:字段读取接口。

3.4. 生成的 Java 类核心结构

PeopleInfo 主类:

  1. 继承 GeneratedMessageV3,只有 get 方法,无 set 方法;
  2. 内置 parseFrom() 系列静态方法:反序列化核心方法(字节数组 / 输入流 → 对象);
  3. 内置 newBuilder() 静态方法:创建对象构造器 Builder;
  4. 继承父接口 MessageLite,拥有 toByteArray()、writeTo():序列化核心方法(对象 → 二进制字节)。

内部 Builder 静态类:

  1. 提供 setXXX() / getXXX() / clearXXX() 方法:读写、清空字段;
  2. 提供 build() 方法:将 Builder 构建为不可变的 PeopleInfo 实体对象。

3.5. 实现序列化与反序列化

java 复制代码
package com.example.start;

import com.google.protobuf.InvalidProtocolBufferException;

import java.util.Arrays;

public class FastStart {
    public static void main(String[] args) {
        // 使用 Builder 构建联系人对象,设置字段值
        PeopleInfo people = PeopleInfo.newBuilder()
                .setName("张三")
                .setAge(21)
                .build();

        // 序列化:对象 → 二进制字节数组
        byte[] byteData = people.toByteArray();
        System.out.println("序列化二进制数组:" + Arrays.toString(byteData));
        System.out.println("序列化数组字节大小:" + byteData.length);

        // 反序列话:二进制字节数组 → 原对象
        try {
            PeopleInfo newPeople = PeopleInfo.parseFrom(byteData);
            System.out.println("反序列化后名字:" + newPeople.getName());
            System.out.println("反序列化后年龄:" + newPeople.getAge());
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        }
    }
}
相关推荐
阿狸猿1 小时前
论大规模分布式系统缓存设计策略
架构
caimouse1 小时前
Reactos 第 9 章 设备驱动 — 9.6 中断处理
网络·windows
摇滚侠2 小时前
SpringMVC 入门到实战 配置类替换 XML 配置文件 86-91
xml·java·后端·spring·maven·intellij-idea
栗子~~2 小时前
金融场景下BigDecimal 运算规范 + 常用场景使用 + 数据库字段设计详解
java·数据库·金融
我登哥MVP2 小时前
SpringCloud Alibaba 核心组件解析:服务注册与发现(Nacos)
java·spring boot·后端·spring·spring cloud·java-ee·maven
兰令水2 小时前
leecodecode【单调栈】【2026.6.12打卡-java版本】
java·开发语言·算法
G_whang2 小时前
AgentMemory — 持久记忆系统:安装、架构与深度使用指南
ai·架构
云烟成雨TD2 小时前
Agent Scope Java 2.x 系列【8】工具调用
java·人工智能·agent
meilindehuzi_a2 小时前
构建基于 RESTful 架构的 TodoList 全栈应用:从前后端理论到 TypeScript/Bun 实战
架构·typescript·restful