Buf CLI 快速入门
设置本地工作区。生成代码以使用您选择的语言实现 API。对文件进行代码检查以提高代码质量和一致性。检测破坏性变更以避免破坏下游消费者。使用集成生成代码的服务器应用程序访问您的 API。要了解如何在大型组织中更有效地使用 Protobuf 模式,请接着完成Buf Schema Registry 快速入门。
Buf CLI 快速入门
Buf CLI 将取代 protoc 成为您处理 Protobuf 文件的主要工具:编译模式、代码检查、检测破坏性变更以及生成代码。在本快速入门中,您将学习:
- 使用
buf.yaml文件配置工作区和模块。 - 生成代码桩,以便与您的 API 集成。
- 对 Protobuf 文件进行代码检查,捕获错误并确保它们遵循最佳实践。
- 检测破坏性变更以保持向后兼容性。
- 实现一个使用生成代码的服务器并调用您的 API。
前提条件
- 如果尚未安装,请安装 Buf CLI。本教程需要 1.32.0 或更高版本,如果您之前安装过 Buf CLI,请检查版本并在必要时更新:
$ buf --version - 确保已安装
git和go,并将它们添加到您的$PATH中。 - 克隆
buf-examples仓库并进入本快速入门的目录:
git clone git@github.com:bufbuild/buf-examples.git
cd buf-examples/cli/quickstart/start/
快速入门包含一个 start 目录(您将在其中处理示例文件)和一个 finish 目录(可用于对照比较)。
配置与构建
首先配置 Buf CLI 并构建定义宠物商店 API 的 .proto 文件,该 API 规定了在商店中创建、获取和删除宠物的方式。
配置工作区
您可以使用 buf.yaml 文件配置 Buf CLI 工作区,该文件定义了您希望视为逻辑单元(即模块)的 Protobuf 文件目录列表。使用以下命令创建该文件:
buf config init
运行命令后,工作区目录中会出现一个 buf.yaml,内容如下:
# 有关 buf.yaml 配置的详细信息,请访问 https://buf.build/docs/configuration/v2/buf-yaml
version: v2
lint:
use:
- STANDARD
breaking:
use:
- FILE
buf.yaml 文件位于工作区的根目录,它所定义的工作区是所有 Buf 操作的默认输入。
更新目录路径并构建模块
生成的 buf.yaml 文件默认使用一个根目录为 .(当前目录)的模块。由于您的 .proto 文件位于 proto/ 子目录中,您需要显式设置模块路径。这样可以限制 Buf 操作仅针对 Protobuf 文件,并将生成的配置和 Go 代码排除在模块根目录之外。使用 modules 键将 proto 目录添加到 buf.yaml 文件中:
version: v2
+modules:
+ - path: proto
lint:
use:
- STANDARD
breaking:
use:
- FILE
在继续之前,请验证所有设置是否正确并且模块可以构建。如果没有错误,说明您已正确配置 Buf 模块:
buf build
如果命令没有输出任何内容,则模块配置正确。
生成代码
Buf CLI 提供了用户友好的本地代码生成体验,且与 protoc 兼容。接下来您将生成一些代码。
配置 buf.gen.yaml 文件
配置好模块后,下一步是创建 buf.gen.yaml 文件来配置本地代码生成。该文件控制 buf generate 命令如何在给定模块上执行 protoc 插件。您可以用它来配置每个 protoc 插件写入结果的位置,并为每个插件指定选项。
在当前目录中创建 buf.gen.yaml 文件,内容如下,以使用 Go 和 Connect-Go 插件生成代码:
version: v2
managed:
enabled: true
override:
- file_option: go_package_prefix
value: github.com/bufbuild/buf-examples/gen
plugins:
- remote: buf.build/protocolbuffers/go
out: gen
opt: paths=source_relative
- remote: buf.build/connectrpc/gosimple
out: gen
opt:
- paths=source_relative
- simple
inputs:
- directory: proto
根据此配置,Buf CLI 会做两件事:
- 执行
protocolbuffers/go插件,为您的.proto文件生成 Go 特定代码,并将其输出放在gen目录中。 - 执行
connectrpc/gosimple插件,为 Connect-Go 生成客户端和服务器桩代码到gen目录。
此配置中有几点需要注意:
- 托管模式 (Managed mode): 托管模式 是一种配置选项,它告诉 Buf CLI 根据一套针对所支持的 Protobuf 语言而设定的意见性值,自动设置您工作区中的所有文件选项。文件选项长期以来一直是 Protobuf 用户困惑和挫败的根源,因此托管模式会根据配置动态设置它们,允许您从
.proto文件中删除它们。 - 远程插件 (Remote plugins): 此处指定的插件是托管在 Buf Schema Registry 上的远程插件。使用它们无需在本地机器上下载、维护或运行插件。
- 输入 (Inputs):
buf generate命令可以接受多种类型的输入,例如 Buf 模块、GitHub 仓库和 tarball/zip 归档。示例代码指向工作区中的proto子目录。
生成 Go 和 Connect RPC 桩代码
现在您已配置好 buf.gen.yaml 文件,就可以生成与 PetStoreService API 关联的 Connect RPC 和 Go 代码。这些插件将从 Buf Schema Registry 获取。请注意,未经身份验证的请求会受到速率限制,因此如果您有 BSR 账户,可以登录以提高限制:
buf registry login
然后运行:
buf generate
如果成功,您会注意到 gen 目录中出现了一些新文件 — 它们就是生成的代码桩:
.
├── gen
│ ├── google
│ │ └── type
│ │ └── datetime.pb.go
│ └── pet
│ └── v1
│ ├── pet.pb.go
│ └── petv1connect
│ └── pet.connect.go
├── proto
│ ├── google
│ │ └── type
│ │ └── datetime.proto
│ └── pet
│ └── v1
│ └── pet.proto
├── buf.gen.yaml
└── buf.yaml
这就是使用 Buf CLI 生成代码的简便方法。无需构建一组复杂的 protoc 命令 — 您的整个配置都包含在 buf.gen.yaml 文件中。
代码检查您的 Protobuf 文件
尽管 Buf CLI 是 protoc 的绝佳替代品,但它远不止是一个 Protobuf 编译器。它还通过 buf lint 命令提供代码检查功能。当您运行 buf lint 时,它会根据指定的代码检查规则集检查 buf.yaml 文件中列出的所有模块。
运行此命令以检查快速入门工作区中的所有 .proto 文件是否存在代码检查错误:
$ buf lint
proto/google/type/datetime.proto:17:1:Package name "google.type" should be suffixed with a correctly formed version, such as "google.type.v1".
proto/pet/v1/pet.proto:56:10:Field name "petID" should be lower_snake_case, such as "pet_id".
proto/pet/v1/pet.proto:61:9:Service name "PetStore" should be suffixed with "Service".
当前宠物商店 API 在两个文件中都存在一些代码检查失败的问题。这些失败违反了 buf.yaml 文件中配置的 STANDARD 类别中的规则。
修复代码检查失败
首先修复 pet/v1/pet.proto 文件中的代码检查失败,这些失败源于 FIELD_LOWER_SNAKE_CASE 和 SERVICE_SUFFIX 规则。结果精确指出了修复错误所需进行的更改,因此请更新 pet.proto 文件:
syntax = "proto3";
package pet.v1;
...
message DeletePetRequest {
- string petID = 1;
+ string pet_id = 1;
}
message DeletePetResponse {}
-service PetStore {
+service PetStoreService {
rpc GetPet(GetPetRequest) returns (GetPetResponse) {}
rpc PutPet(PutPetRequest) returns (PutPetResponse) {}
rpc DeletePet(DeletePetRequest) returns (DeletePetResponse) {}
}
再次运行 buf lint 以确认其中两个失败问题已解决:
$ buf lint
proto/google/type/datetime.proto:17:1:Package name "google.type" should be suffixed
with a correctly formed version, such as "google.type.v1".
由于您更改了 petID 字段和服务名称,需要重新生成代码桩:
buf generate
忽略代码检查失败
google/type/datetime.proto 实际上不是您本地项目中的文件。相反,它是您的依赖项之一,由 googleapis 提供,因此您无法更改其 package 声明来修复代码检查失败。相反,您可以通过以下配置更改告诉 Buf CLI 忽略 google/type/datetime.proto 文件:
version: v2
modules:
- path: proto
lint:
use:
- STANDARD
+ ignore:
+ - proto/google/type/datetime.proto
breaking:
use:
- FILE
最后再运行一次 buf lint,应该不再有错误。
检测破坏性变更
Buf CLI 还能让您检测 API 不同版本之间的破坏性变更。buf breaking 命令会根据指定的破坏性变更规则集检查 buf.yaml 文件中列出的所有模块,并与 Protobuf 模式的旧版本进行比较。这些规则是可选的,并根据您关心的破坏性变更类型分成逻辑类别:
FILE:检测会导致生成代码在文件之间移动的变更,从而破坏基于文件的生成源代码。PACKAGE:检测会破坏基于包的生成源代码变更的变更。它检测会破坏生成桩代码的变更,但仅考虑包级别的变更。WIRE_JSON:检测会破坏 wire(二进制)或 JSON 编码的变更。由于 JSON 无处不在,我们建议将此作为最低级别。WIRE:检测会破坏 wire(二进制)编码的变更。
默认值为 FILE,我们建议使用此值以保证 API 消费者之间的最大兼容性。我们建议仅选择其中一个选项,而不是像指定代码检查配置那样包含/排除特定的破坏性变更规则。您当前的 buf.yaml 文件已配置了 FILE 选项:
version: v2
modules:
- path: proto
lint:
use:
- STANDARD
ignore:
- proto/google/type/datetime.proto
breaking:
use:
- FILE
破坏您的 API
要查看该功能的实际效果,您需要引入一个破坏性变更。首先,在 WIRE 级别进行一项破坏性变更。这是最基本的破坏性变更类型,因为它会改变 Protobuf 消息在传输(“on the wire”)中的编码方式。这种类型的破坏性变更会影响所有语言的所有用户。
将 Pet.pet_type 字段的类型从 PetType 更改为 string:
message Pet {
- PetType pet_type = 1;
+ string pet_type = 1;
string pet_id = 2;
string name = 3;
}
运行 buf breaking
现在,通过运行 buf breaking 来验证这是一个破坏性变更,方法是选择一个输入与您的工作区进行比较。在本例中,您将与本地的 main Git 分支进行比较。路径后的 #key=value 片段语法是 Buf 指定输入修饰符的方式:branch=main 选择 Git 分支,subdir=cli/quickstart/start/proto 将比较范围限制在该分支内的 proto 子目录中。
# --against 输入指向克隆仓库 main 分支中的 'proto' 子目录。
# '../../../' 从 cli/quickstart/start/ 返回到仓库根目录。
$ buf breaking --against "../../../.git#branch=main,subdir=cli/quickstart/start/proto"
proto/pet/v1/pet.proto:1:1:Previously present service "PetStore" was deleted from file.
proto/pet/v1/pet.proto:32:3:Field "1" with name "pet_type" on message "Pet" changed type from "enum" to "string".
proto/pet/v1/pet.proto:47:3:Field "1" with name "pet_type" on message "PutPetRequest" changed type from "enum" to "string".
proto/pet/v1/pet.proto:56:3:Field "1" with name "pet_id" on message "DeletePetRequest" changed option "json_name" from "petID" to "petId".
proto/pet/v1/pet.proto:56:10:Field "1" on message "DeletePetRequest" changed name from "petID" to "pet_id".
第二和第三个错误是您刚刚所做的更改 — PetType 同时用于 Pet 和 PutPetRequest 消息中。其他三个错误来自您之前应用的代码检查修复 — 将 petID 重命名为 pet_id 以及将 PetStore 重命名为 PetStoreService 也是 wire 级别和生成代码级别的破坏性变更。在实际项目中,您需要解决所有这些问题,但为了本快速入门,您可以暂时忽略它们。
还原更改
确定您的更改是破坏性变更后,请将其还原:
message Pet {
- string pet_type = 1;
+ PetType pet_type = 1;
string pet_id = 2;
string name = 3;
}
实现一个 API
在本节中,您将实现一个 PetStoreService 客户端和服务器,两者都可以在命令行上运行。
初始化 go.mod 文件
在编写 Go 代码之前,使用 go mod init 命令初始化 go.mod 文件:
go mod init github.com/bufbuild/buf-examples
模块路径与克隆的仓库匹配,以便生成的代码和服务器代码中的导入路径能够正确解析。在您自己的项目中,这将是您的实际模块路径(例如 github.com/your-org/your-repo)。与 buf.yaml 文件类似,go.mod 文件跟踪代码的 Go 依赖项。
实现服务器
通过创建 server/main.go 文件开始实现服务器:
$ mkdir server
$ touch server/main.go
将以下内容复制并粘贴到该文件中:
package main
import (
"context"
"fmt"
"log"
"net/http"
petv1 "github.com/bufbuild/buf-examples/gen/pet/v1"
"github.com/bufbuild/buf-examples/gen/pet/v1/petv1connect"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)
const address = "localhost:8080"
func main() {
mux := http.NewServeMux()
path, handler := petv1connect.NewPetStoreServiceHandler(&petStoreServiceServer{})
mux.Handle(path, handler)
fmt.Println("... Listening on", address)
http.ListenAndServe(
address,
// 使用 h2c 以便无需 TLS 即可提供 HTTP/2 服务。
h2c.NewHandler(mux, &http2.Server{}),
)
}
// petStoreServiceServer 实现了 PetStoreService API。
type petStoreServiceServer struct {
petv1connect.UnimplementedPetStoreServiceHandler
}
// PutPet 将给定请求关联的宠物添加到宠物商店中。
func (s *petStoreServiceServer) PutPet(
_ context.Context,
req *petv1.PutPetRequest,
) (*petv1.PutPetResponse, error) {
pet := &petv1.Pet{Name: req.GetName(), PetType: req.PetType}
log.Printf("PutPet received a %v named %s", pet.GetPetType(), pet.GetName())
return &petv1.PutPetResponse{Pet: pet}, nil
}
解析 Go 依赖项
现在您已经有了服务器代码,运行以下命令来解析构建代码所需的依赖项:
$ go mod tidy
调用 API
使用上面 server/main.go 的实现,运行服务器并使用 buf CLI 调用 PutPet 端点。
首先,运行服务器:
go run server/main.go
... Listening on localhost:8080
在另一个终端中,在工作区根目录下,使用 buf curl 调用 API 将宠物添加到商店:
buf curl \
--schema . \
--data '{"pet_type": "PET_TYPE_SNAKE", "name": "Ekans"}' \
http://localhost:8080/pet.v1.PetStoreService/PutPet
{
"pet": {
"petType": "PET_TYPE_SNAKE",
"name": "Ekans"
}
}
--schema . 告诉 buf curl 使用本地工作区来解析请求和响应的 Protobuf 模式。如果没有它,buf curl 将需要服务器支持 gRPC server reflection。
请注意,响应使用了小驼峰命名字段名(petType 而不是 pet_type)— 这是标准的 proto3 JSON 编码,它将 snake_case 的 Protobuf 字段名映射为 lowerCamelCase。
回到服务器终端窗口,您会看到请求已被接收:
2025/06/13 10:06:25 PutPet received a PET_TYPE_SNAKE named Ekans
总结
这是一个基本的演练,您已经学习了 Buf CLI 的关键功能:
- 设置本地工作区。
- 生成代码以使用您选择的语言实现 API。
- 对文件进行代码检查以提高代码质量和一致性。
- 检测破坏性变更以避免破坏下游消费者。
- 使用集成生成代码的服务器应用程序访问您的 API。
要了解如何在大型组织中更有效地使用 Protobuf 模式,请接着完成 Buf Schema Registry 快速入门。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)