Protobuf 语法精讲与 gRPC 概念
Protobuf 语法与 gRPC 概念 一、⚡ Protobuf 是什么:为什么非要学一门"新语言"? 前面讲了 JSON 序列化——Jackson 和 Fastjson2 把 Java 对象和 JSON 之间互转。JSON 是人眼可读的文本——但文本格式有两个天然的劣势: JSON 消息体(108 字节): { "orderId": 10001, "userId": 2001, "productName": "iPhone 15", "amount": 6999.00 } 同样的信息——Protobuf 二进制(约 35 字节): \x08\x91N\x12\x04...(肉眼不可读的二进制) JSON Protobuf 文本格式——每个字符占 1 字节 二进制格式——用最少的字节表达同样的数据 字段名重复传输("orderId" 每次都要传) 字段名不传——用数字编号代替(orderId = 1) 解析慢——文本解析器 解析快——二进制解码器 人眼可读——curl 能调 人眼不可读——需要专用工具 Protobuf 省的不是几字节——是高并发场景下每一条消息都省 60%~80% 带宽。这就是为什么 gRPC 选择 Protobuf 作为默认序列化格式。 二、🧬 Protobuf 语法逐一拆解 Protobuf 的语法就是定义数据结构和服务接口的语言。文件后缀是 .proto。先看一个完整的例子,然后每个语法元素拆开讲: // order.proto —— 订单服务的完整 proto 定义 syntax = "proto3"; // ① 语法版本 package com.example.order; // ② 包名 option java_multiple_files = true; // ③ 编译选项 option java_package = "com.example.order.dto"; import "google/protobuf/timestamp.proto"; // ④ 导入其他 proto // ⑤ 枚举定义 enum OrderStatus { ORDER_STATUS_UNSPECIFIED = 0; // 枚举第一个值必须是 0 ORDER_STATUS_CREATED = 1; ORDER_STATUS_PAID = 2; ORDER_STATUS_CANCELLED = 3; } // ⑥ 消息定义——Protobuf 的核心 message Order { int64 order_id = 1; // 字段编号 = 1 string product_name = 2; // 字段编号 = 2 double amount = 3; // 字段编号 = 3 OrderStatus status = 4; // 使用上面定义的枚举 google.protobuf.Timestamp created_at = 5; // 使用导入的时间戳类型 repeated string tags = 6; // repeated = 数组/列表 map<string, string> metadata = 7; // map 类型 } // ⑦ 服务定义——gRPC 的方法声明 service OrderService { rpc GetOrder(GetOrderRequest) returns (Order); rpc ListOrders(ListOrdersRequest) returns (stream Order); // 服务端流 rpc CreateOrder(stream CreateOrderRequest) returns (stream Order); // 双向流 } // 消息定义可以在 service 之后——顺序不要求 message GetOrderRequest { int64 order_id = 1; } message ListOrdersRequest { int64 user_id = 1; int32 page_size = 2; } message CreateOrderRequest { string product_name = 1; double amount = 2; } 2.1 syntax —— 声明语法版本 syntax = "proto3"; // 必须在文件第一行(注释上面可以,语法上面不能有东西) 版本 特点 当前状态 proto2 老版本——有 required/optional/default 关键字 gRPC 也支持,但不推荐新项目用 proto3 新版本——去掉了 required 和 default,所有字段默认可选 当前主流——新项目统一用 proto3 2.2 package —— 包名 package com.example.order; // 防止命名冲突——和 Java 的 package 概念一样 // 生成 Java 代码时:类放在 com.example.order 包下 2.3 option —— 编译选项 // java_multiple_files: true → 每个 message 生成一个独立的 .java 文件 // false(默认) → 所有 message 生成到一个巨大的外部类中 option java_multiple_files = true; // java_package: 指定生成的 Java 文件所在的包——如果不写,用 package 的值 option java_package = "com.example.order.dto"; // java_outer_classname: java_multiple_files = false 时——指定外部类的类名 option java_outer_classname = "OrderProto"; // optimize_for: 生成代码优化方向——SPEED / CODE_SIZE / LITE_RUNTIME option optimize_for = SPEED; 选项 默认值 建议 java_multiple_files false true——每个 message 一个文件,方便 IDE 导航 java_package package 的值(但它是默认的,不是必然) 写成和 Java 项目一致的包名 optimize_for SPEED 保持默认 2.4 import —— 导入其他 proto 文件 import "google/protobuf/timestamp.proto"; // 导入 Google 内置类型 import "common/common.proto"; // 导入自己项目的公共 proto import public "common/new.proto"; // 公开导入——谁 import 你,谁也看得到 new.proto 的类型 常用内置类型(google/protobuf/ 下的类型): ...