Go Opaque API 迁移
Opaque API 是 Go 编程语言 Protocol Buffers 实现的最新版本。旧版本现在称为 Open Struct API。有关介绍,请参阅 Go Protobuf:发布 Opaque API 这篇博文。
向 Opaque API 的迁移是增量进行的,可以基于每个 proto 消息或每个 .proto
文件,通过将 api_level
特性设置为其可能的值之一来实现:
API_OPEN
选择 Open Struct API。此项已向后移植到 2023 版,因此旧版本的 Go 插件可能不支持它。API_HYBRID
是 Open 和 Opaque 之间的一个过渡步骤:混合 API 也包含访问器方法(以便您可以更新代码),但仍然像以前一样导出结构体字段。性能上没有差异;此 API 级别仅用于辅助迁移。API_OPAQUE
选择 Opaque API;这是 2024 版及更新版本的默认设置。
要为特定的 .proto
文件覆盖默认设置,请设置 api_level
特性:
edition = "2024";
package log;
import "google/protobuf/go_features.proto";
option features.(pb.go).api_level = API_OPEN;
message LogEntry { … }
在将现有文件的 api_level
更改为 API_OPAQUE
之前,需要更新所有对已生成 proto 代码的现有用法。open2opaque
工具可以帮助完成此操作。
为方便起见,您还可以使用 protoc
命令行标志覆盖默认的 API 级别:
protoc […] --go_opt=default_api_level=API_OPEN
要为特定文件 (而不是所有文件) 覆盖默认 API 级别,请使用 apilevelM
映射标志 (类似于用于导入路径的 M
标志):
protoc […] --go_opt=apilevelMhello.proto=API_OPEN
自动化迁移
我们努力使您将现有项目迁移到 Opaque API 的过程尽可能简单:我们的 open2opaque 工具会完成大部分工作!
要安装迁移工具,请使用:
go install google.golang.org/open2opaque@latest
注意
如果您在使用自动化迁移方法时遇到任何问题,请参阅Opaque API:手动迁移指南。项目准备
确保您的构建环境和项目使用的是足够新的 Protocol Buffers 和 Go Protobuf 版本:
从 protobuf 发布页面 将 protobuf 编译器 (protoc) 更新到 29.0 或更新版本。
将 protobuf 编译器 Go 插件 (protoc-gen-go) 更新到 1.36.0 或更新版本:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
在每个项目中,更新
go.mod
文件以使用 1.36.0 或更新版本的 protobuf 模块:go get google.golang.org/protobuf@latest
注意
如果您尚未导入google.golang.org/protobuf
,您可能仍在使用旧版模块。请参阅google.golang.org/protobuf
公告(2020 年),并在返回此页面之前迁移您的代码。
步骤 1:切换到混合 API
使用 open2opaque
工具将您的 .proto
文件切换到混合 API:
open2opaque setapi -api HYBRID $(find . -name "*.proto")
您现有的代码将继续构建。混合 API 是 Open API 和 Opaque API 之间的一个步骤,它添加了新的访问器方法,但保持结构体字段可见。
步骤 2:open2opaque rewrite
要重写您的 Go 代码以使用 Opaque API,请运行 open2opaque rewrite
命令:
open2opaque rewrite -levels=red github.com/robustirc/robustirc/...
您可以指定一个或多个包或模式。
举个例子,如果您有如下代码:
logEntry := &logpb.LogEntry{}
if req.IPAddress != nil {
logEntry.IPAddress = redactIP(req.IPAddress)
}
logEntry.BackendServer = proto.String(host)
该工具会将其重写为使用访问器:
logEntry := &logpb.LogEntry{}
if req.HasIPAddress() {
logEntry.SetIPAddress(redactIP(req.GetIPAddress()))
}
logEntry.SetBackendServer(host)
另一个常见示例是使用结构体字面量初始化 protobuf 消息:
return &logpb.LogEntry{
BackendServer: proto.String(host),
}
在 Opaque API 中,等效的做法是使用 Builder:
return logpb.LogEntry_builder{
BackendServer: proto.String(host),
}.Build()
该工具将其可用的重写分为不同级别。-levels=red
参数启用所有重写,包括那些需要人工审查的。可用级别如下:
- 绿色 (green): 安全的重写(高置信度)。包括工具所做的大部分更改。这些更改不需要仔细查看,甚至可以由自动化提交,无需任何人工监督。
- 黄色 (yellow): (合理置信度)这些重写需要人工审查。它们应该是正确的,但请您审查一下。
- 红色 (red): 潜在危险的重写,更改罕见且复杂的模式。这些需要仔细的人工审查。例如,当一个现有函数接受一个
*string
参数时,如果该函数意图通过写入指针 (*foo = "value"
) 来更改字段值,那么典型的修复方法proto.String(msg.GetFoo())
将不起作用。
许多程序仅需绿色级别的更改即可完全迁移。在您可以将 proto 消息或文件迁移到 Opaque API 之前,您需要完成所有级别的所有重写,此时您的代码中将不再有直接的结构体访问。
步骤 3:迁移和验证
要完成迁移,请使用 open2opaque
工具将您的 .proto
文件切换到 Opaque API:
open2opaque setapi -api OPAQUE $(find . -name "*.proto")
现在,任何尚未被重写为 Opaque API 的剩余代码将不再能编译。
运行您的单元测试、集成测试和其他验证步骤(如果有)。
疑问?问题?
首先,请查看 Opaque API 常见问题解答。如果这不能回答您的问题或解决您的问题,请参阅我可以在哪里提问或报告问题?