Go Opaque API 迁移

描述了到 Opaque API 的自动化迁移过程。

Opaque API 是 Protocol Buffers 在 Go 编程语言中的最新实现版本。旧版本现在称为 Open Struct API。请参阅 Go Protobuf:发布 Opaque API 博客文章了解介绍。

到 Opaque API 的迁移是增量进行的,基于每个 proto 消息或每个 `.proto` 文件,通过将 Protobuf 版本功能 `api_level` 选项设置为其可能值之一来实现

  • API_OPEN 选择 Open Struct API;这是 2024 年 12 月之前唯一的 API。
  • API_HYBRID 是 Open 和 Opaque 之间的一个步骤:Hybrid API 也包含访问器方法(因此您可以更新代码),但仍然像以前一样导出结构体字段。没有性能差异;此 API 级别仅有助于迁移。
  • API_OPAQUE 选择 Opaque API。

目前,默认设置为 API_OPEN,但即将发布的 Protobuf 2024 版本 将把默认设置更改为 API_OPAQUE

在 2024 版本之前使用 Opaque API,请如下设置 api_level

edition = "2023";

package log;

import "google/protobuf/go_features.proto";
option features.(pb.go).api_level = API_OPAQUE;

message LogEntry {  }

在将现有文件的 api_level 更改为 API_OPAQUE 之前,需要更新所有现有生成的 proto 代码的使用方式。open2opaque 工具可以帮助完成此工作。

为了您的方便,您还可以使用 protoc 命令行标志覆盖默认的 API 级别

protoc […] --go_opt=default_api_level=API_OPAQUE

要为特定文件(而不是所有文件)覆盖默认的 API 级别,请使用 apilevelM 映射标志(类似于 用于导入路径的 M 标志

protoc […] --go_opt=apilevelMhello.proto=API_OPAQUE

命令行标志也适用于仍使用 proto2 或 proto3 语法的 `.proto` 文件,但如果您想从 `.proto` 文件内部选择 API 级别,则需要先将该文件迁移到版本化。

自动化迁移

我们努力让您将现有项目迁移到 Opaque API 变得尽可能容易:我们的 open2opaque 工具完成了大部分工作!

要安装迁移工具,请使用

go install google.golang.org/open2opaque@latest

项目准备

确保您的构建环境和项目使用的 Protocol Buffers 和 Go Protobuf 版本足够新

  1. protobuf 发布页面更新 protobuf 编译器 (protoc) 到 29.0 或更高版本。

  2. 更新 protobuf 编译器 Go 插件 (protoc-gen-go) 到 1.36.0 或更高版本

    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
    
  3. 在每个项目中,更新 go.mod 文件以使用 1.36.0 或更高版本的 protobuf 模块

    go get google.golang.org/protobuf@latest
    

步骤 1:切换到 Hybrid API

使用 open2opaque 工具将您的 `.proto` 文件切换到 Hybrid API

open2opaque setapi -api HYBRID $(find . -name "*.proto")

您现有的代码将继续构建。Hybrid API 是 Open 和 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 参数时,使用 proto.String(msg.GetFoo()) 的典型修复方法可能无效,如果该函数通过写入指针(*foo = "value")来改变字段值。

许多程序仅通过绿色级别的更改即可完全迁移。在将 proto 消息或文件迁移到 Opaque API 之前,您需要完成所有级别的所有重写,此时您的代码中将不再保留直接结构体访问。

步骤 3:迁移和验证

要完成迁移,请使用 open2opaque 工具将您的 `.proto` 文件切换到 Opaque API

open2opaque setapi -api OPAQUE $(find . -name "*.proto")

现在,任何尚未重写为 Opaque API 的剩余代码将不再编译。

运行您的单元测试、集成测试和其他验证步骤(如果有)。

有疑问?有问题?

首先,请查看 Opaque API 常见问题。如果那里没有回答您的问题或解决您的问题,请参阅 我在哪里可以提问或报告问题?