service 与 rpc 定义
在 .proto 文件中,service 块定义服务名称,其中每个 rpc 语句定义一个具体的方法。
service UserService {
// 1. Unary RPC — 最普通的请求/响应模式
rpc GetUser(GetUserRequest) returns (UserResponse);
// 2. 服务端流式 — 服务端持续推送多条消息
rpc ListUsers(ListUsersRequest) returns (stream UserResponse);
// 3. 客户端流式 — 客户端发送多条后等待一次响应
rpc BatchCreateUsers(stream CreateUserRequest) returns (BatchResult);
// 4. 双向流式 — 全双工,双方都可随时发消息
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}
项目目录结构
myapp/
├── proto/ # .proto 源文件
│ └── user/v1/
│ └── user.proto
├── gen/ # 生成的代码(不要手动修改)
│ └── user/v1/
│ ├── user.pb.go # 消息结构体
│ └── user_grpc.pb.go # gRPC 服务接口
├── server/ # 服务端实现
│ └── main.go
├── client/ # 客户端代码
│ └── main.go
├── buf.yaml # buf 配置
├── buf.gen.yaml # buf 代码生成配置
└── go.mod
protoc 命令生成代码
Go 代码生成
# 生成 Go 消息结构体 + gRPC 服务代码
protoc \
--proto_path=proto \
--go_out=gen \
--go_opt=paths=source_relative \
--go-grpc_out=gen \
--go-grpc_opt=paths=source_relative \
user/v1/user.proto
# 参数说明:
# --proto_path=proto 指定 .proto 文件搜索路径
# --go_out=gen 生成 .pb.go 文件到 gen 目录
# paths=source_relative 保持 proto 文件的相对路径结构
# --go-grpc_out=gen 生成 _grpc.pb.go 文件
Python 代码生成
# 生成 Python 消息类 + gRPC 存根
python3 -m grpc_tools.protoc \
-I proto \
--python_out=gen_py \
--grpc_python_out=gen_py \
user/v1/user.proto
# 生成文件:
# gen_py/user/v1/user_pb2.py — 消息类
# gen_py/user/v1/user_pb2_grpc.py — 客户端/服务端存根
生成的 Go 文件解读
// user.pb.go — 由 protoc-gen-go 生成
package userv1
// proto message → Go struct
type User struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
// 不可见的内部字段(不要直接访问)
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
// user_grpc.pb.go — 由 protoc-gen-go-grpc 生成
// 客户端接口(调用方使用)
type UserServiceClient interface {
GetUser(ctx context.Context, in *GetUserRequest,
opts ...grpc.CallOption) (*UserResponse, error)
ListUsers(ctx context.Context, in *ListUsersRequest,
opts ...grpc.CallOption) (UserService_ListUsersClient, error)
}
// 服务端接口(实现方必须实现)
type UserServiceServer interface {
GetUser(context.Context, *GetUserRequest) (*UserResponse, error)
ListUsers(*ListUsersRequest, UserService_ListUsersServer) error
mustEmbedUnimplementedUserServiceServer()
}
// 嵌入此结构以获得未实现方法的默认错误处理
type UnimplementedUserServiceServer struct{}
UnimplementedUserServiceServer:在实现服务端时,务必嵌入 UnimplementedUserServiceServer。这是为了向前兼容——当 .proto 文件新增 RPC 方法时,未实现的方法会返回 UNIMPLEMENTED 错误而不是编译失败,给你时间逐步迁移。
buf 工具:现代 protoc 替代品
buf 解决了 protoc 的诸多痛点:依赖管理混乱、lint 规则分散、breaking change 无法检测。
buf.yaml — 项目配置
# buf.yaml
version: v2
modules:
- path: proto
lint:
use:
- DEFAULT # 启用默认 lint 规则集
breaking:
use:
- FILE # 检测文件级别的 breaking change
buf.gen.yaml — 代码生成配置
# buf.gen.yaml
version: v2
plugins:
- remote: buf.build/protocolbuffers/go
out: gen
opt:
- paths=source_relative
- remote: buf.build/grpc/go
out: gen
opt:
- paths=source_relative
常用 buf 命令
# 生成代码(替代 protoc)
buf generate
# lint 检查(发现不规范的 proto 写法)
buf lint
# 检测 breaking change(与 git 历史对比)
buf breaking --against '.git#branch=main'
# 格式化 .proto 文件
buf format -w
# 从 Buf Schema Registry 拉取依赖
buf dep update
实战:定义完整的用户服务
// proto/user/v1/user.proto
syntax = "proto3";
package user.v1;
option go_package = "github.com/myapp/gen/user/v1;userv1";
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
enum UserRole {
USER_ROLE_UNSPECIFIED = 0;
USER_ROLE_GUEST = 1;
USER_ROLE_MEMBER = 2;
USER_ROLE_ADMIN = 3;
}
message User {
string id = 1;
string name = 2;
string email = 3;
UserRole role = 4;
google.protobuf.Timestamp created_at = 5;
}
message GetUserRequest { string id = 1; }
message UserResponse { User user = 1; }
message ListUsersRequest {
int32 page_size = 1;
string page_token = 2;
}
message CreateUserRequest {
string name = 1;
string email = 2;
UserRole role = 3;
}
message DeleteUserRequest { string id = 1; }
service UserService {
rpc GetUser (GetUserRequest) returns (UserResponse);
rpc ListUsers (ListUsersRequest) returns (stream UserResponse);
rpc CreateUser (CreateUserRequest) returns (UserResponse);
rpc DeleteUser (DeleteUserRequest) returns (google.protobuf.Empty);
}
本章小结:service/rpc 定义是 gRPC 的服务契约,protoc 或 buf 将其转化为各语言的类型安全代码。掌握 buf 工具链可以大幅提升团队协作效率,尤其是 lint 和 breaking change 检测,是专业团队的必备工具。