阶段四:gRPC
Protocol Buffers(Protobuf)是gRPC的基石——它既是接口定义语言(IDL),也是高效的二进制序列化格式。Proto文件定义了服务的契约,自动生成的代码保证类型安全。本课深入Proto3语法、字段类型、消息设计模式、编码原理和最佳实践。
// proto文件基本结构
syntax = "proto3"; // 指定版本(proto3是当前推荐版本)
package ecommerce; // 包名,防止命名冲突
option go_package = "./pb"; // Go代码生成选项
option java_package = "com.example.ecommerce"; // Java选项
// 导入其他proto文件
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
// 服务定义
service OrderService {
rpc GetOrder(GetOrderRequest) returns (Order);
rpc ListOrders(ListOrdersRequest) returns (ListOrdersResponse);
rpc CreateOrder(CreateOrderRequest) returns (Order);
}
// 消息定义
message Order {
int32 id = 1; // 字段编号
string order_number = 2; // 字符串
google.protobuf.Timestamp created_at = 3; // 使用Well-Known类型
OrderStatus status = 4; // 枚举
repeated OrderItem items = 5; // 重复字段(数组)
map<string, string> metadata = 6; // Map
oneof payment { // Oneof(互斥字段)
CreditCardPayment credit_card = 7;
WechatPayment wechat = 8;
AlipayPayment alipay = 9;
}
}
| Proto类型 | Java类型 | Go类型 | 说明 |
|---|---|---|---|
| double | double | float64 | 双精度浮点 |
| float | float | float32 | 单精度浮点 |
| int32 | int | int32 | 变长编码,负数效率低 |
| int64 | long | int64 | 变长编码 |
| sint32 | int | int32 | ZigZag编码,负数高效 |
| sint64 | long | int64 | ZigZag编码 |
| uint32 | int | uint32 | 无符号变长 |
| uint64 | long | uint64 | 无符号变长 |
| fixed32 | int | uint32 | 定长4字节,值>2^28高效 |
| fixed64 | long | uint64 | 定长8字节 |
| bool | boolean | bool | 布尔 |
| string | String | string | UTF-8字符串 |
| bytes | ByteString | []byte | 字节数组 |
enum OrderStatus {
// proto3第一个枚举值必须是0(默认值)
ORDER_STATUS_UNSPECIFIED = 0; // 推荐:用前缀+UNSPECIFIED作默认值
ORDER_STATUS_PENDING = 1;
ORDER_STATUS_PROCESSING = 2;
ORDER_STATUS_SHIPPED = 3;
ORDER_STATUS_DELIVERED = 4;
ORDER_STATUS_CANCELLED = 5;
}
// ⚠️ 枚举值不要复用已删除的编号!
// 如果删除了OLD_STATUS = 3,不要把新枚举设为3
// 使用reserved保留
enum Color {
reserved 2, 3; // 保留编号
reserved "RED", "BLUE"; // 保留名称
COLOR_UNSPECIFIED = 0;
COLOR_GREEN = 1;
COLOR_YELLOW = 4;
}
message Product {
int32 id = 1;
string name = 2;
// repeated = 数组/列表
repeated string tags = 3; // string数组
repeated ProductImage images = 4; // 对象数组
// map = 键值对
map<string, string> attributes = 5; // 属性映射
map<int32, DiscountRule> rules = 6; // 编号→规则
// ⚠️ map的key不能是float/bytes/enum/message
// ⚠️ repeated不能嵌套map(repeated map<...>不允许)
}
message ProductImage {
string url = 1;
int32 width = 2;
int32 height = 3;
}
message DiscountRule {
double discount = 1;
string description = 2;
}
// oneof:同一时刻只能设置其中一个字段
message Payment {
int32 id = 1;
double amount = 2;
oneof method {
CreditCardPayment credit_card = 3;
WechatPayment wechat = 4;
AlipayPayment alipay = 5;
BankTransfer bank_transfer = 6;
}
}
message CreditCardPayment {
string card_number = 1; // 实际应加密
string expiry = 2;
}
message WechatPayment {
string open_id = 1;
string transaction_id = 2;
}
message AlipayPayment {
string user_id = 1;
string trade_no = 2;
}
message BankTransfer {
string bank_name = 1;
string account = 2;
}
// 嵌套消息
message Order {
int32 id = 1;
// 嵌套定义
message Address {
string street = 1;
string city = 2;
string province = 3;
string zip_code = 4;
}
Address shipping_address = 2;
Address billing_address = 3;
// 外部引用嵌套类型: Order.Address
}
// Well-Known类型(google/protobuf/)
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/any.proto";
import "google/protobuf/wrappers.proto";
message EnhancedOrder {
int32 id = 1;
google.protobuf.Timestamp created_at = 2; // 精确时间
google.protobuf.Duration processing_time = 3; // 持续时间
google.protobuf.Any extra = 4; // 任意类型
google.protobuf.Int32Value priority = 5; // 可空int32(wrapper)
}
// ecommerce.proto - 完整电商订单Proto
syntax = "proto3";
package ecommerce;
option go_package = "./pb";
import "google/protobuf/timestamp.proto";
import "google/protobuf/field_mask.proto";
// ===== 枚举 =====
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0;
ORDER_STATUS_PENDING = 1;
ORDER_STATUS_CONFIRMED = 2;
ORDER_STATUS_PROCESSING = 3;
ORDER_STATUS_SHIPPED = 4;
ORDER_STATUS_DELIVERED = 5;
ORDER_STATUS_CANCELLED = 6;
ORDER_STATUS_REFUNDED = 7;
}
enum PaymentStatus {
PAYMENT_STATUS_UNSPECIFIED = 0;
PAYMENT_STATUS_PENDING = 1;
PAYMENT_STATUS_PAID = 2;
PAYMENT_STATUS_FAILED = 3;
PAYMENT_STATUS_REFUNDED = 4;
}
// ===== 核心消息 =====
message Order {
int32 id = 1;
string order_number = 2;
int32 user_id = 3;
OrderStatus status = 4;
PaymentStatus payment_status = 5;
repeated OrderItem items = 6;
ShippingInfo shipping = 7;
PaymentInfo payment = 8;
double subtotal = 9; // 商品小计
double discount = 10; // 折扣金额
double shipping_fee = 11; // 运费
double total = 12; // 总计
string note = 13;
map<string, string> metadata = 14;
google.protobuf.Timestamp created_at = 15;
google.protobuf.Timestamp updated_at = 16;
}
message OrderItem {
int32 id = 1;
int32 product_id = 2;
string product_name = 3;
string sku = 4;
double unit_price = 5;
int32 quantity = 6;
double subtotal = 7;
map<string, string> attributes = 8; // 颜色/尺码等
}
message ShippingInfo {
string recipient = 1;
string phone = 2;
Address address = 3;
string carrier = 4;
string tracking_number = 5;
google.protobuf.Timestamp shipped_at = 6;
google.protobuf.Timestamp delivered_at = 7;
}
message Address {
string province = 1;
string city = 2;
string district = 3;
string street = 4;
string zip_code = 5;
}
message PaymentInfo {
string payment_method = 1;
string transaction_id = 2;
double amount = 3;
google.protobuf.Timestamp paid_at = 4;
}
// ===== 请求/响应 =====
message GetOrderRequest { int32 id = 1; }
message ListOrdersRequest {
int32 user_id = 1;
OrderStatus status = 2;
int32 page = 3;
int32 page_size = 4;
}
message ListOrdersResponse {
repeated Order orders = 1;
int32 total = 2;
}
message CreateOrderRequest {
int32 user_id = 1;
repeated CreateOrderItem items = 2;
ShippingInfo shipping = 3;
string payment_method = 4;
string note = 5;
string coupon_code = 6;
}
message CreateOrderItem {
int32 product_id = 1;
int32 quantity = 2;
map<string, string> attributes = 3;
}
message UpdateOrderRequest {
int32 id = 1;
OrderStatus status = 2;
string note = 3;
google.protobuf.FieldMask update_mask = 4; // 部分更新
}
message CancelOrderRequest {
int32 id = 1;
string reason = 2;
}
// ===== 服务定义 =====
service OrderService {
rpc GetOrder(GetOrderRequest) returns (Order);
rpc ListOrders(ListOrdersRequest) returns (ListOrdersResponse);
rpc CreateOrder(CreateOrderRequest) returns (Order);
rpc UpdateOrder(UpdateOrderRequest) returns (Order);
rpc CancelOrder(CancelOrderRequest) returns (Order);
}
service ProductQueryService {
rpc GetProduct(GetProductRequest) returns (Product);
rpc ListProducts(ListProductsRequest) returns (ListProductsResponse);
}
message Product {
int32 id = 1;
string name = 2;
string category = 3;
double price = 4;
int32 stock = 5;
repeated string tags = 6;
}
message GetProductRequest { int32 id = 1; }
message ListProductsRequest {
string category = 1;
string search = 2;
int32 page = 3;
int32 page_size = 4;
}
message ListProductsResponse {
repeated Product products = 1;
int32 total = 2;
}
// Protobuf编码:Varint + Tag-Value
// 字段编号 + 类型 → Tag
// Tag = (field_number << 3) | wire_type
// wire_type: 0=Varint, 1=64-bit, 2=Length-delimited, 5=32-bit
// 示例:int32 id = 1; 值=150
// Tag = (1 << 3) | 0 = 0x08
// Varint(150) = 0x9601
// 编码结果: 08 96 01 (3字节)
// 对比JSON: {"id":150} = 9字节
// Protobuf节省了67%的空间!
// 字段编号选择:
// 1-15: 1字节Tag(高频字段务必使用)
// 16-2047: 2字节Tag
// ⚠️ 19000-19999保留给Protobuf内部使用
message CompactMessage {
int32 high_freq_field = 1; // 1字节Tag ✅
string another_high = 2; // 1字节Tag ✅
bool flag = 3; // 1字节Tag ✅
int32 low_freq = 16; // 2字节Tag(可接受)
string very_low = 1000; // 2字节Tag(低频可以)
}
为社交网络定义Proto:用户、帖子、评论、点赞、关注、消息。注意oneof(消息类型)和枚举设计。
用Node.js验证Protobuf的编解码,对比JSON和Protobuf的大小差异。
const protobuf = require('protobufjs');
const root = protobuf.loadSync('ecommerce.proto');
const Order = root.lookupType('ecommerce.Order');
const payload = { id: 1, orderNumber: "ORD-001", status: "ORDER_STATUS_PENDING" };
const msg = Order.create(payload);
const buffer = Order.encode(msg).finish();
const jsonSize = JSON.stringify(payload).length;
console.log(`JSON: ${jsonSize} bytes, Protobuf: ${buffer.length} bytes`);