Protobuf IDL 支持
本页说明 Apache Fory 如何处理 Protocol Buffers(.proto)schema、protobuf 概念如何映射到 Fory,以及 protobuf 专用 Fory 扩展选项的使用方式。
本页内容
- 如何在具体场景下选择 protobuf 或 Fory
- 迁移时需要关注的语法与语义差异
.proto文件中支持的 Fory 扩展选项- 从 protobuf 迁移到 Fory 的实践路径
快速决策指南
| 场景 | 建议格式 |
|---|---|
| 主要构建 gRPC API,依赖 protobuf 工具链 | Protocol Buffers |
| 需要极致对象图性能与引用跟踪 | Fory |
| 需要在序列化数据中表达循环/共享引用 | Fory |
| 需要强 unknown-field 语义保证线格式兼容 | Protocol Buffers |
| 希望直接使用原生 struct/class,而非 protobuf 包装类型 | Fory |
Protobuf 与 Fory 对比
| 维度 | Protocol Buffers | Fory |
|---|---|---|
| 主要目标 | RPC/消息契约 | 高性能对象序列化 |
| 编码模型 | Tag-Length-Value | Fory 二进制协议 |
| 引用跟踪 | 非内建 | 一等支持(ref) |
| 循环引用 | 不支持 | 支持 |
| 未知字段 | 保留 | 不保留 |
| 生成类型 | protobuf 专用模型类型 | 语言原生构造 |
| gRPC 生态 | 原生成熟 | 持续建设中(活跃开发) |
Fory 的 gRPC 支持仍在持续开发中。当前生产级 gRPC 工作流里,protobuf 仍是更成熟的默认选择。
为什么使用 Apache Fory
- 代码更贴近语言习惯:Fory IDL 生成的类和结构体可直接作为业务领域对象使用。
- 序列化性能更高:在 Fory 基准中,Fory 在对象序列化场景中可显著快于 protobuf。
- 对象图表达更自然:共享引用和循环引用是内建能力,无需通过业务层 ID 关联绕过。
性能细节请参见 性能参考。
语法与语义映射
Package 与文件级选项
Protocol Buffers
syntax = "proto3";
package example.models;
option java_package = "com.example.models";
option go_package = "example.com/models";
Fory
package example.models;
Fory 使用统一 package 命名空间做跨语言注册。语言特定的包路径仍可在代码生成阶段单独配置。
Message 与 Enum 定义
Protocol Buffers
message User {
string id = 1;
string name = 2;
optional string email = 3;
int32 age = 4;
repeated string tags = 5;
map<string, string> metadata = 6;
}
enum Status {
STATUS_UNSPECIFIED = 0;
STATUS_ACTIVE = 1;
}
Fory
message User [id=101] {
string id = 1;
string name = 2;
optional string email = 3;
int32 age = 4;
list<string> tags = 5;
map<string, string> metadata = 6;
}
enum Status [id=102] {
UNKNOWN = 0;
ACTIVE = 1;
}
关键差异:
- Fory 可直接分配稳定类型 ID(
[id=...])。 - Fory 使用
list<T>(repeated T为兼容别名)。 - 枚举命名更偏向语言习惯,而非 protobuf 前缀风格。
oneof 到 union
protobuf 的 oneof 会被翻译为嵌套 Fory union,并增加一个可选字段指向该 union。
Protocol Buffers
message Event {
oneof payload {
string text = 1;
int32 number = 2;
}
}
转换后的 Fory 结构
message Event {
union payload {
string text = 1;
int32 number = 2;
}
optional payload payload = 1;
}
说明:
- union case ID 来自原
oneof字段号。 - 自动生成的 union 引用字段使用
oneof中最小字段号。
Import 与 Well-Known Types
支持 protobuf import。常见 well-known types 会直接映射:
google.protobuf.Timestamp->timestampgoogle.protobuf.Duration->durationgoogle.protobuf.Any->any
类型映射要点
| Protobuf Type | Fory 映射 |
|---|---|
bool | bool |
int32, uint32 | 可变长 32 位整型家族 |
sint32 | zigzag 32 位整型 |
int64, uint64 | 可变长 64 位整型家族 |
sint64 | zigzag 64 位整型 |
fixed32, fixed64 | 定长无符号整型家族 |
sfixed32, sfixed64 | 定长有符号整型家族 |
float, double | float32, float64 |
string, bytes | string, bytes |
repeated T | list<T> |
map<K, V> | map<K, V> |
optional T | optional T |
oneof | union + 可选 union 引用字段 |
int64 [(fory).type = "tagged_int64"] | tagged_int64 编码 |
uint64 [(fory).type = "tagged_uint64"] | tagged_uint64 编码 |
Fory 扩展选项(Protobuf)
.proto 文件中的 Fory 专用选项使用 (fory). 前缀。
option (fory).enable_auto_type_id = true;
message TreeNode {
TreeNode parent = 1 [(fory).weak_ref = true];
repeated TreeNode children = 2 [(fory).ref = true];
}
文件级选项
| 选项 | 类型 | 说明 |
|---|---|---|
(fory).use_record_for_java_message | bool | 对该文件所有 message 生成 Java record |
(fory).polymorphism | bool | 默认开启多态序列化元信息 |
(fory).enable_auto_type_id | bool | 缺失时自动生成类型 ID(编译器默认 true) |
(fory).evolving | bool | message 的默认 schema 演进行为 |
(fory).go_nested_type_style | string | Go 嵌套命名风格:underscore(默认)或 camelcase |
Message 与 Enum 级选项
| 选项 | 作用对象 | 类型 | 说明 |
|---|---|---|---|
(fory).id | message, enum | int | 显式类型 ID(用于注册) |
(fory).alias | message, enum | string | 自动 ID 哈希使用的别名 |
(fory).evolving | message | bool | 覆盖文件级演进设置 |
(fory).use_record_for_java | message | bool | 为该 message 生成 Java record |
(fory).deprecated | message, enum | bool | 标记类型为弃用 |
(fory).namespace | message | string | 覆盖默认 package 命名空间 |
字段级选项
| 选项 | 类型 | 说明 |
|---|---|---|
(fory).ref | bool | 为该字段启用引用跟踪 |
(fory).nullable | bool | 将字段视为可空(optional) |
(fory).weak_ref | bool | 生成弱指针语义(C++/Rust 代码生成) |
(fory).thread_safe_pointer | bool | ref 字段在 Rust 中的指针类型(Arc vs Rc) |
(fory).deprecated | bool | 标记字段为弃用 |
(fory).type | string | 基础类型覆盖,目前支持 tagged_int64/tagged_uint64 |
引用相关行为:
weak_ref = true隐含开启 ref 跟踪。- 对
repeated字段,(fory).ref = true作用于列表元素。 - 对
map<K, V>字段,(fory).ref = true作用于 map value。 weak_ref与thread_safe_pointer是 C++/Rust 代码生成提示。
典型选项组合示例
message Graph {
Node root = 1 [(fory).ref = true, (fory).thread_safe_pointer = false];
repeated Node nodes = 2 [(fory).ref = true];
map<string, Node> cache = 3 [(fory).ref = true];
Node parent = 4 [(fory).weak_ref = true];
}