gRPC 接口设计建议 - 处理创建和更新的正确样式

gRPC interface design advice - correct style for handling creates and updates

我们正在构建一个新平台,其中 gRPC 提供消息传递层,最终将公开我们 API 预期支持的全套功能。

我们正在尝试确定命名接口的最佳模式,以避免重复的消息类型,不得不繁重地处理边缘情况

我们正在做的一个简单示例涉及创建、更新和检索用户。这是我们今天的服务的示例:

service Users {
  rpc GetUser(UserRequest) returns (core.user.User) {}
  rpc ListUsers(google.protobuf.Empty) returns (ListUsersResponse) {}
  rpc CreateUser(core.user.User) returns (core.user.User) {}
  rpc UpdateUser(core.user.User) returns (core.user.User) {}
}

message UserRequest {
  string id = 1;
}

message ListUsersResponse{
  repeated core.user.User users = 1;
}

GetUser 非常简单 - 它接受一个简单的 UserRequest 消息,其中包含一个 ID,returns 一个用户(来自我们的核心包 - 应用程序中的许多服务将接受一个用户消息作为输入,所以我们将其放在共享位置)。

我的问题专门针对 Create/Update 用户调用,因为目前尚不清楚最佳解决方案是什么。这两个函数略有不同,主要在于在一种情况下我们已经有一个用户,因此有一个 ID,而在另一种情况下我们正在创建一个新用户。在 Create 案例中,我们可能只有 User 上可能存在的可用字段的一个子集——但理想情况下,我们只需要在一个地方维护这个列表,因为它可能会变得相当大。我们可以:

代码

message CreateUserRequest{ 
  core.User user = 1;
}
message UpdateUserRequest{
  int32 id = 1;
  core.User user = 2;
}

我正在努力在网上找到许多其他人如何处理这类问题的例子。我提供的示例相当简单,但您可以想象在整个项目中我们都会遇到类似的问题。我希望看到的是某人在实践中完成的相当复杂的 gRPC 接口的示例,或者只是来自广泛使用它的人的反馈,以了解他们认为围绕接口设计的哪些模式最有效。

谢谢!

我想你要找的是Google's networked API Design Guide. Look at the Naming Conventions。特别是该页面上的方法名称部分。您将看到与您尝试做的非常相似的示例,这恰好很常见。

有关更具体的示例,请查看如何 etcd has written their API here. Similar to your CreateUserRequest and UpdateUserRequest etcd has MemberAddRequest and MemberUpdateRequest

您还可以查看 UBER 的在线设计文档,其中提供了单独响应实施的理由,并提供了详细的版本控制和命名约定