Protobuf 是什么?一篇文章搞懂这个高性能序列化神器

简介: Protobuf是Google开源的高效二进制序列化协议,体积小、速度快,支持跨语言、向后兼容。相比JSON,更适合RPC等高性能场景,广泛应用于微服务通信。通过`.proto`文件定义结构,自动生成代码,实现数据的快速序列化与反序列化。

Protobuf 是什么?一篇文章搞懂这个高性能序列化神器

在聊 RPC 的时候,我们提到它会用 Protobuf 这类序列化协议来提升性能。很多人第一次听到 Protobuf 会有点懵:它到底是什么?和 JSON 有啥区别?为什么 RPC 都爱用它?今天咱们就把这些问题说透。


一、Protobuf 本质:高效的「二进制序列化协议」

Protobuf 全称 Protocol Buffers,是 Google 开源的一种语言无关、平台无关、可扩展的序列化机制。简单来说,它的核心作用就是:

把内存里的复杂数据结构(比如对象、结构体),转换成紧凑的二进制字节流,方便网络传输或持久化存储;反过来,也能把字节流还原成原来的数据结构(反序列化)。

🌰 生活化比喻:JSON 是书信,Protobuf 是电报

  • JSON:就像用自然语言写的书信,内容清晰易读,但有很多冗余的字符(比如 {}""、逗号),体积大,传输慢。
  • Protobuf:就像用密文写的电报,只有懂「密码本」(.proto 定义文件)的人才能看懂,体积小、传输快,但可读性差。

二、Protobuf 核心优势:为什么 RPC 都爱用它?

相比 JSON、XML 这类文本序列化格式,Protobuf 有几个关键优势,刚好契合 RPC 「高性能、跨语言、可兼容」的需求:

1. 🚀 体积更小、速度更快

Protobuf 是二进制格式,没有 JSON 里的冗余字符,相同数据的体积比 JSON 小 3~10 倍,序列化 / 反序列化速度快 20~100 倍。

举个例子:

  • 一个包含 name(字符串)、age(整数)、email(字符串)的用户对象,用 JSON 序列化后可能是:

    {
         "name":"张三","age":25,"email":"zhangsan@example.com"}
    
  • 用 Protobuf 序列化后,只是一串二进制字节,长度更短,解析更快。

这种性能提升在高并发的微服务场景下(比如 RPC 调用),能显著降低网络带宽消耗和延迟。

2. 🌍 跨语言、跨平台

只要你用 .proto 文件定义好数据结构,就能通过 Protobuf 编译器(protoc)生成 Java、Python、Go、C++ 等几十种语言的代码。

这意味着:

  • 你的微服务可以用 Go 写,另一个用 Java 写,它们通过 Protobuf 序列化后,就能互相通信,不用关心语言差异。
  • 这对大型分布式系统、多语言混合开发的场景非常友好。

3. 🛡️ 向后兼容,服务升级不翻车

Protobuf 支持增量更新字段,且保证新旧版本兼容:

  • 新增字段时,旧版本的客户端会自动忽略新增的字段;
  • 旧版本的字段被删除时,新版本的客户端会给缺失的字段用默认值(比如 int32 默认为 0,string 默认为空)。

这种兼容性让微服务升级时不用停服,也不用同步更新所有依赖的服务,大大降低了运维风险。

4. 📊 强类型,避免类型错误

.proto 文件里,你需要明确定义每个字段的类型(比如 int32stringbool),编译器会生成强类型的代码。

比如定义一个用户消息:

message User {
  string name = 1;   // 字段编号1,类型string
  int32 age = 2;     // 字段编号2,类型int32
  string email = 3;  // 字段编号3,类型string
}

生成的代码会严格校验类型,避免了 JSON 里常见的「字符串转数字失败」等问题。


三、Protobuf 工作流程:从定义到使用(Java 版本)

1. 编写 .proto 文件:定义数据结构

先保持 .proto 文件内容不变(这是跨语言的核心,也是 Protobuf 的优势),文件名建议命名为 user.proto

syntax = "proto3";  // 使用Protobuf 3版本
package user;       // 包名,避免命名冲突
option java_package = "com.example.protobuf";  // 生成Java代码的包路径
option java_outer_classname = "UserProto";     // 生成的外层类名(必须大写开头)

// 定义用户消息
message User {
  string name = 1;
  int32 age = 2;
  repeated string tags = 3;  // repeated表示数组/列表类型
}

// 定义获取用户的请求和响应
message GetUserRequest {
  int32 user_id = 1;
}

message GetUserResponse {
  User user = 1;
  bool success = 2;
  string error_msg = 3;
}

注意:Java 版本需要额外添加 java_packagejava_outer_classname 两个选项,指定生成代码的包路径和外层类名,否则会按默认规则生成,不利于项目管理。

2. 用 protoc 生成 Java 代码

前置条件:

生成命令:

user.proto 文件所在目录执行以下命令:

# 生成Java代码,--java_out 后指定生成代码的输出目录(这里是当前目录下的src/main/java)
protoc --java_out=./src/main/java user.proto

执行完成后,会在 src/main/java/com/example/protobuf/ 目录下生成 UserProto.java 文件,这个文件包含了所有消息类的定义和序列化 / 反序列化方法,无需手动修改。

3. 在 Java 代码中使用(核心示例)

第一步:引入 Protobuf 依赖(Maven/Gradle)

Java 项目需要引入 Protobuf 的核心依赖,才能使用生成的代码:

Maven(pom.xml)

<dependencies>
    <!-- Protobuf 核心依赖 -->
    <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-java</artifactId>
        <version>3.25.3</version>  <!-- 建议和protoc版本一致 -->
    </dependency>
</dependencies>

Gradle(build.gradle)

dependencies {
   
    implementation 'com.google.protobuf:protobuf-java:3.25.3'
}

第二步:编写 Java 示例代码

package com.example.protobuf;

import com.google.protobuf.InvalidProtocolBufferException;

public class ProtobufJavaDemo {
   
    public static void main(String[] args) throws InvalidProtocolBufferException {
   
        // ====================== 1. 序列化:将对象转为二进制字节流 ======================
        // 1.1 创建User对象(通过Builder构建,Protobuf生成的类默认不可变)
        UserProto.User user = UserProto.User.newBuilder()
                .setName("张三")       // 设置字符串字段
                .setAge(25)           // 设置整数字段
                .addTags("程序员")     // 添加数组元素(单个添加)
                .addTags("Java")      // 继续添加数组元素
                .build();             // 构建不可变的User对象

        // 1.2 序列化:调用toByteArray()方法,得到二进制字节数组
        byte[] serializedData = user.toByteArray();
        System.out.println("序列化后的字节数组长度:" + serializedData.length);  // 体积远小于JSON

        // ====================== 2. 反序列化:将二进制字节流转回对象 ======================
        // 2.1 反序列化:调用parseFrom()方法,从字节数组还原User对象
        UserProto.User deserializedUser = UserProto.User.parseFrom(serializedData);

        // 2.2 验证反序列化结果
        System.out.println("反序列化后的姓名:" + deserializedUser.getName());
        System.out.println("反序列化后的年龄:" + deserializedUser.getAge());
        System.out.println("反序列化后的标签列表:" + deserializedUser.getTagsList());
    }
}

代码关键解释:

  • 对象构建:Protobuf 生成的 Java 类是不可变的,必须通过 newBuilder() 构建器模式创建对象,设置字段后调用 build() 得到最终对象;
  • 序列化核心方法toByteArray() —— 将对象转为二进制字节数组(网络传输 / 存储的核心格式);
  • 反序列化核心方法parseFrom(byte[] data) —— 从字节数组还原对象,会抛出 InvalidProtocolBufferException(字节流损坏 / 格式错误时触发);
  • 数组字段操作repeated 类型在 Java 中对应 List,通过 addTags() 单个添加,或 addAllTags(List<String>) 批量添加,获取时用 getTagsList()

运行结果示例:

序列化后的字节数组长度:20
反序列化后的姓名:张三
反序列化后的年龄:25
反序列化后的标签列表:[程序员, Java]

Java 中使用 Protobuf 需先通过 protoc 生成代码,且 .proto 文件要指定 java_packagejava_outer_classname 确保代码结构规范;

核心操作:通过 newBuilder() 构建对象,toByteArray() 序列化,parseFrom() 反序列化;

Protobuf 生成的 Java 类是不可变的,数组字段需通过 addXXX()/addAllXXX() 操作,获取时用 getXXXList()


四、Protobuf vs JSON:怎么选?

特性 Protobuf JSON
格式 二进制 文本
体积 小(3~10 倍于 JSON)
速度 快(20~100 倍于 JSON)
可读性 差(需要工具解析) 好(人类可直接阅读)
跨语言 原生支持 原生支持
向后兼容 原生支持 需要自己实现
适用场景 RPC、微服务、内部通信 前后端交互、API 对外提供

简单总结:

  • 内部服务通信(如 RPC):选 Protobuf,追求性能和效率。
  • 对外 API、前后端交互:选 JSON,追求可读性和兼容性。

五、总结

Protobuf 是为「高性能、跨语言、可兼容」的序列化场景设计的,尤其适合 RPC、微服务、分布式系统等需要高效通信的场景。它不是要取代 JSON,而是在特定场景下提供更好的选择。

搞懂它的核心优势和工作流程,你就能明白为什么 RPC 框架(如 gRPC、Dubbo)都把 Protobuf 作为默认序列化协议了。

目录
相关文章
|
3月前
|
XML 存储 JSON
Protocol Buffers (Protobuf) 详解
Protocol Buffers(Protobuf)是Google推出的高效数据序列化格式,语言无关、平台无关,比JSON和XML更小更快。支持多语言代码生成,具备良好的兼容性与类型安全,广泛应用于gRPC、微服务通信及数据存储等场景。
538 5
|
Ubuntu Linux C语言
Ubuntu下安装vscode,并解决终端打不开vscode的问题
Ubuntu下安装vscode,并解决终端打不开vscode的问题
2097 0
|
1月前
|
存储 人工智能 运维
向量数据库实战指南:从部署到RAG落地
本文以轻量开源向量数据库Chroma为例,手把手带你完成环境部署、数据导入、相似性检索到RAG集成的全流程实战,避开新手常见坑,适配码农与大数据爱好者快速落地AI应用,助力掌握向量数据库核心技能。
|
2月前
|
人工智能 监控 算法
AI搜索引擎内容、GEO优化工具开发工程的“可信赖”基石:内容真实性、权威性与ADSM工程化闭环
在AI搜索主导信息入口的今天,生成式引擎优化(GEO)成为新焦点。内容不仅是流量载体,更是可信赖的知识资产。依托ADSM技术框架,最新上架的GEO特工队AI等工具实现算法洞察、真实性验证与权威投放闭环,确保品牌内容在豆包、千问等平台中成为“黄金信源”,构建长期可信认知。
287 0
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
AI切文章就像切西瓜:递归字符分割让机器懂你心
你有没有试过给ChatGPT发一篇超长文章,结果它说'太长了,看不完'?就像让人一口吃下整个西瓜一样不现实!递归字符分割技术就像一个贴心的切瓜师傅,知道在哪里下刀才不会破坏瓜的甜美。掌握这项技术,让你的AI应用从'消化不良'变成'营养吸收专家'。#人工智能 #文本处理 #自然语言处理 #机器学习
240 4
|
1月前
|
网络协议 Dubbo Java
从 TCP 到 RPC:彻底搞懂「HTTP 与 RPC用法区别」
本文深入剖析HTTP与RPC的本质区别,从TCP底层原理讲起,解析粘包拆包、协议封装等核心问题,梳理二者演进脉络。通过对比服务发现、传输性能、适用场景等维度,结合Dubbo、gRPC等框架,帮你按场景精准选型,彻底搞懂微服务通信的技术逻辑。
212 5
|
1月前
|
安全 应用服务中间件 Linux
HTTPS 优化完整方案解析
本文详解HTTPS性能优化全方案,从原理到实操,涵盖硬件加速(AES-NI)、软件升级(内核与OpenSSL)及协议层优化(TLS 1.3、ECDSA、会话复用等),配合Nginx配置模板与验证方法,助你实现安全与速度双提升,显著降低访问延迟。
898 2
|
2月前
|
人工智能 JavaScript 安全
使用Cursor自动生成完整函数的指南
本文分享了如何利用Cursor的AI功能快速生成可用的函数代码。以提取Markdown图片链接为例,通过分步描述需求、迭代优化并生成测试用例,演示了从简单实现到生产级代码的完整流程。文章总结了分步细化需求、明确输入输出及生成测试等最佳实践,并提醒仍需人工审查以确保代码安全与业务逻辑正确。
|
4月前
|
设计模式 算法 搜索推荐
Java 设计模式之策略模式:灵活切换算法的艺术
策略模式通过封装不同算法并实现灵活切换,将算法与使用解耦。以支付为例,微信、支付宝等支付方式作为独立策略,购物车根据选择调用对应支付逻辑,提升代码可维护性与扩展性,避免冗长条件判断,符合开闭原则。
881 35