Protobuf — 这是什么,二进制数据格式和工作原理

作者: IT Sectr 发布日期: 2026-03-08 阅读时间: 8 分钟

Protocol Buffers (Protobuf) 是 Google 开发的一种结构化数据二进制序列化格式,用于服务之间的高效信息交换。该格式要求在.proto文件中预先定义模式,从而生成不同语言的代码。根据Google官方文档,Protobuf确保消息大小比JSON小3-10倍。Protobuf用于gRPC、Google Maps和数千个内部服务。

核心要点

  • Protobuf — Google的二进制序列化格式,数据表示紧凑
  • .proto模式 —精确类型的数据结构描述,可编译为代码
  • 性能 —序列化速度比JSON快3-10倍,更紧凑
  • 向后兼容 —模式演进而不破坏现有客户端
  • gRPC —微服务中Protobuf的主要传输技术

什么是Protocol Buffers?

Protocol Buffers 是 Google 开发的一种结构化数据序列化机制,类似于 JSON 和 XML,但有根本不同:数据以二进制格式编码。这意味着Protobuf消息无法用肉眼直接阅读,但占用更少空间,处理速度也比文本形式更快。

Protobuf 是在 Google 内部创建的,用于解决服务之间数据交换时的性能问题。2008年,该技术成为开源项目,支持多种语言:C++、Java、Python、Go、JavaScript、Kotlin、Swift、Dart等。2016年发布的proto3版本简化了语法并增加了对更多语言的支持。

Protobuf 和 JSON 之间的关键差异是在开始数据交换之前需要定义模式。模式文件(.proto)描述消息的结构:字段、类型和唯一字段编号。protoc编译器从这个模式生成目标语言的类,用于序列化和反序列化。

Protobuf生态系统

  • gRPC —高性能RPC框架,默认使用Protobuf
  • protoc —.proto模式编译器,可输出多种编程语言代码
  • Google API —多数 Google 公开API使用Protobuf
  • Envoy Proxy —使用Protobuf进行配置和数据交换

Protobuf架构

Protobuf架构包括三个关键组件:模式定义语言(.proto)、protoc编译器和特定语言的运行时库。开发者在.proto文件中描述数据结构,运行编译后获得处理这些数据的已准备好的类。

Protobuf消息中的每个字段都有唯一编号(field number)—这不是序号,而是二进制格式中用于识别字段的标记。1到15号用一个字节编码,16到2047号用两个字节。因此,使用频率高的重要字段应编号1到15以优化大小。

Protobuf数据类型

Protobuf支持广泛的数据类型:标量类(int32, int64, float, double, bool, string, bytes)、枚举(enum)、复合(message)和特殊(oneof, map)。每种类型都有特定的二进制表示,优化于相应的使用场景。

.proto类型 C++类型 Java/Kotlin 说明
doubledoubledouble64位浮点数
floatfloatfloat32位浮点数
int32int32int32位,variable-length encoding
int64int64long64位,variable-length encoding
stringstringStringUTF-8字符串
bytesstringByteString任意字节
boolboolbooleantrue / false

二进制格式的优势

Protobuf相比于文本格式有三个关键优势:消息大小、序列化速度和严格类型。在高负载系统和流量有限的移动应用中,这些优势变得至关重要。

Protobuf的二进制表示使用variable-length encoding(Varint)来处理数字:小数字占用1个字节,大数字最多占用10个字节。这可以高效编码标识符、标志位和计数器,而这些在JSON或XML中作为文本字符串会占用数十个字节。例如,数字150在Protobuf中占用2个字节,在JSON中占用3个字节(作为文本“150”),在XML中占用3个字节加标签。

额外的优势是向后兼容。在模式中添加新字段不会破坏旧客户端:它们只是忽略未知字段。删除字段只需要保留其编号以避免未来冲突。

定义.proto模式

.proto模式使用专门语言描述数据结构。文件从指定语法(proto3)、包名和导入依赖开始。每个消息使用message关键字定义,带有字段,每个字段有类型、名称和唯一编号。

良好模式的规则:经常使用的字段使用1到15号,有意义的名称,相关字段分组到单独的message中,可能是多个变量之一的字段使用oneof。

protobuf
syntax = "proto3";

package mobileapp;

message User {
  string user_id = 1;
  string name = 2;
  string email = 3;
  int32 age = 4;
  UserRole role = 5;
  repeated string tags = 6;
  map<string, string> metadata = 7;
}

enum UserRole {
  USER_ROLE_UNSPECIFIED = 0;
  USER_ROLE_USER = 1;
  USER_ROLE_ADMIN = 2;
  USER_ROLE_MODERATOR = 3;
}

嵌套消息

消息可以嵌套:Profile类型的profile字段将包含用户数据。嵌套支持可以描述复杂的层级结构而无需重复定义。

protobuf
syntax = "proto3";

package mobileapp;

message Order {
  string order_id = 1;
  repeated OrderItem items = 2;
  double total_price = 3;
  PaymentInfo payment = 4;
}

message OrderItem {
  string product_id = 1;
  string title = 2;
  int32 quantity = 3;
  double price = 4;
}

message PaymentInfo {
  string method = 1;
  string transaction_id = 2;
  double amount = 3;
}

从.proto生成代码

protoc编译器将.proto文件转换为目标语言的源代码。对于Kotlin/Java使用--java_out参数,Swift使用--swift_out(通过Apple Swift Protobuf插件),Dart使用--dart_out。生成的类包含构建消息的builder方法和序列化/反序列化方法。

在Android上,通过Gradle中的com.google.protobuf插件(版本0.9+)来使用Protobuf。添加插件并指定.proto文件后,编译过程会自动生成可用于应用程序的Kotlin类。

kotlin
import com.google.protobuf.kotlin.toByteString
import com.example.mobileapp.UserOuterClass.User

fun createUser(): User {
    return User.newBuilder()
        .setUserId("usr_001")
        .setName("IT Sectr")
        .setEmail("team@itsectr.com")
        .setAge(5)
        .setRole(UserOuterClass.UserRole.USER_ROLE_ADMIN)
        .addTags("mobile")
        .addTags("backend")
        .build()
}

fun serializeAndDeserialize(user: User): User {
    // 序列化为二进制格式
    val bytes = user.toByteArray()

    // 从二进制格式反序列化
    return User.parseFrom(bytes)
}

Protobuf使用示例

Protobuf在微服务架构和移动应用中特别高效。考虑一个典型场景:移动应用通过gRPC从服务器接收产品列表。Protobuf消息包含产品信息,包括标识符、名称、价格和分类。二进制格式将响应大小减少5-8倍。

Kotlin中的gRPC服务

服务端Protobuf使用gRPC服务的示例。服务声明GetProducts RPC方法,接收带分页参数的请求并返回产品列表。Kotlin实现使用生成的类来处理请求和响应。

kotlin
import com.example.mobileapp.ProductServiceGrpcKt
import com.example.mobileapp.ProductOuterClass.Product
import com.example.mobileapp.ProductOuterClass.GetProductsRequest
import com.example.mobileapp.ProductOuterClass.GetProductsResponse

class ProductService : ProductServiceGrpcKt.ProductServiceCoroutineImplBase() {

    override suspend fun getProducts(
        request: GetProductsRequest
    ): GetProductsResponse {
        val products = fetchProductsFromDb(
            page = request.page,
            limit = request.limit
        )

        return GetProductsResponse.newBuilder()
            .addAllProducts(products)
            .setTotalCount(products.size)
            .build()
    }
}

在Android项目中配置Protobuf

配置Protobuf通过Gradle插件com.google.protobuf在Android项目中完成。插件在编译时自动启动protoc编译器,从.proto文件生成Kotlin类。需要在项目级别的build.gradle中添加插件并在应用模块中应用。

配置Gradle后,.proto文件放置在src/main/proto目录中。protoc编译器在每次编译时处理它们,生成可用于应用代码的Kotlin类。正确指定protobuf和protoc版本很重要,以避免与项目其他库产生依赖冲突。

对于iOS项目,通过CocoaPods或Swift Package Manager连接Protobuf。Swift Protobuf插件自动生成符合Codable协议的Swift结构。对于Flutter的Dart项目,使用protobuf包,编译通过dart run protoc_plugin完成。

常见问题

Protobuf和JSON之间的区别是什么?

Protobuf是带必要模式的二进制格式,比JSON紧凑3-10倍。JSON是文本格式,人可读,不需要模式。Protobuf序列化和反序列化更快,但需要编译.proto文件。JSON更容易调试,不需要预先配置。

如何安装protoc编译器?

从protobuf的GitHub发行版中为您的平台下载protoc。macOS通过brew install protobuf安装。Windows下载zip压缩包并将protoc.exe添加到PATH。Android/Kotlin使用com.google.protobuf Gradle插件,它在构建时自动启动编译。

什么是gRPC,它和Protobuf有什么关系?

gRPC是Google的高性能RPC框架,使用Protobuf作为接口语言(IDL)和序列化格式。gRPC在.proto文件中定义服务和RPC方法,生成客户端和服务端代码,支持流式传输和二进制协议。

Protobuf支持哪些语言?

Google官方支持:C++、Java、Kotlin、Python、Go、Ruby、C#、PHP、JavaScript、Objective-C、Swift和Dart。社区开发了对Rust、TypeScript、Scala、Lua等语言的支持。移动开发可使用Kotlin/Java(Android)和Swift/Objective-C(iOS)。

如何确保模式的向后兼容?

Protobuf通过以下规则支持向后兼容:不要修改字段编号,不要删除字段(使用reserved),使用新编号添加新字段。旧客户端忽略未知字段,新客户端为缺失的旧字段获取默认值。

总结

  • Protocol Buffers — Google的二进制序列化格式,带必要数据模式
  • 消息大小 由于Varint编码比JSON小3-10倍
  • 速度 序列化和反序列化超过文本格式
  • .proto模式 为12+种语言提供严格类型和代码生成
  • 向后兼容 允许API演进而不破坏客户端
  • gRPC 使用Protobuf作为微服务通信的标准格式
  • 理想 适合高负载系统和流量有限的移动应用

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读