跳到主要内容

Proto API 定义最佳实践

1. 基本设计原则

  • 保持简单:API 的设计应尽量简洁,避免过度复杂的嵌套结构。
  • 向后兼容:确保 API 的更新不会破坏现有客户端。
  • 明确语义:字段和消息的命名应清晰,表达其用途。

2. 文件结构

  • 模块化设计:将 .proto 文件按功能模块拆分,避免单个文件过大。
  • 统一命名空间:使用 package 定义命名空间,避免命名冲突。
    syntax = "proto3";

    package myapp.user.v1; // 包名应包含版本号
  • 版本管理:在包名或服务名中包含版本号,方便未来的版本升级。
    package myapp.user.v1; // 包含版本号

3. 字段设计

  • 字段编号:字段编号应从 1 开始,且不能重复或更改已使用的编号。
    message User {
    int32 id = 1; // 字段编号从 1 开始
    string name = 2; // 不要更改已使用的编号
    }
  • 字段类型:选择合适的字段类型,避免使用 optional 和 required(Proto3 中已移除)。
    message User {
    string email = 3; // 使用 string 表示文本
    bool is_active = 4; // 使用 bool 表示布尔值
    }
  • 字段命名:使用 snake_case 命名字段,保持一致性。
    message User {
    string first_name = 1;
    string last_name = 2;
    }
  • 避免删除字段:如果字段不再使用,可以标记为废弃,但不要删除,以保持向后兼容。
    message User {
    string deprecated_field = 5 [deprecated = true];
    }

4. 服务定义

  • 明确服务职责:每个服务应专注于单一职责,避免过多的 RPC 方法。
    service UserService {
    rpc GetUser(GetUserRequest) returns (GetUserResponse);
    rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
    }
  • 方法命名:使用动词短语命名方法,清晰表达操作意图。
    rpc GetUser(GetUserRequest) returns (GetUserResponse);
    rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);
  • 请求和响应消息:每个 RPC 方法应有独立的请求和响应消息,便于扩展。
    message GetUserRequest {
    int32 user_id = 1;
    }

    message GetUserResponse {
    User user = 1;
    }

5. 错误处理

  • 使用 gRPC 状态码:通过 gRPC 的状态码(如 NOT_FOUND、INVALID_ARGUMENT)传递错误信息。
  • 扩展错误信息::在响应中添加 error_details 字段,提供详细的错误描述。
    message ErrorResponse {
    string error_message = 1;
    int32 error_code = 2;
    }

6. 扩展性

  • 使用 oneof:对于互斥字段,使用 oneof 提高可读性和扩展性。
    message SearchRequest {
    oneof query {
    string user_id = 1;
    string email = 2;
    }
    }
  • 嵌套消息:将相关的消息嵌套在一起,避免命名冲突。
    message CreateUserRequest {
    message Address {
    string street = 1;
    string city = 2;
    }
    Address address = 1;
    }

7. 注释和文档

  • 添加注释:为每个字段、消息和服务添加注释,方便生成文档。
    // 用户信息
    message User {
    // 用户 ID
    int32 id = 1;
    // 用户姓名
    string name = 2;
    }
  • 使用工具生成文档:使用工具(如 protoc-gen-doc[1])生成 API 文档。

8. 性能优化

  • 避免嵌套过深:深层嵌套会增加解析复杂度,影响性能。
  • 使用流式传输:对于大数据量或实时数据,使用流式 RPC。
    rpc StreamUsers(StreamUsersRequest) returns (stream User);

9. 示例

syntax = "proto3";

package myapp.user.v1;

// 用户服务
service UserService {
// 获取用户信息
rpc GetUser(GetUserRequest) returns (GetUserResponse);
// 创建用户
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
}

// 用户信息
message User {
int32 id = 1; // 用户 ID
string name = 2; // 用户姓名
string email = 3; // 用户邮箱
}

// 请求和响应消息
message GetUserRequest {
int32 user_id = 1; // 用户 ID
}

message GetUserResponse {
User user = 1; // 用户信息
}

message CreateUserRequest {
string name = 1; // 用户姓名
string email = 2; // 用户邮箱
}

message CreateUserResponse {
User user = 1; // 创建的用户信息
}

总结

通过遵循这些最佳实践,可以设计出高效、可扩展且易于维护的 gRPC API,同时确保向后兼容性和良好的开发体验。 主要内容整理自:

引用

[1] protoc-gen-doc