使用 gRPC,我应该如何处理服务器和客户端之间的部分结果?
Posted
技术标签:
【中文标题】使用 gRPC,我应该如何处理服务器和客户端之间的部分结果?【英文标题】:Using gRPC, how should I handle partial results between my server and client? 【发布时间】:2021-05-14 15:05:14 【问题描述】:这更像是一个 API 设计问题,但我很好奇其他人对这种情况的看法:
假设我有一台服务器,客户端将向我发送他们想要检索数据的项目列表,我返回 item:data 的映射。
当然首先我会检查我的缓存,查询我的数据库。让我们假设数据库查询花费的时间太长(无论出于何种原因),所以我有足够的时间终止以将部分结果(在缓存中找到)返回给客户端。
我想让客户知道两件重要的事情:
-
这是您的部分结果(如果您选择使用它们)。
这就是我无法解决您的其余问题的原因 (
err.ConnDeadlineExceeded
)
现在我计划只返回部分结果和一个 nil 错误。原因是我的大多数客户首先检查err != nil
,只有当这是真的时,他们才会真正费心查看结果。
但这感觉不对,因为我正在删除有关数据库超时的信息。有什么建议的解决方案吗?
【问题讨论】:
【参考方案1】:正如您所确定的,这完全取决于您希望客户端如何响应 API 响应:
results, err := app.Api(ctx, queryMessage)
部分结果指示器将驻留在results
或非零err
。
以ldapsearch
为例 - 如果您提供大小限制 - API 将返回最多该数量的结果,但返回非零返回码 (sizeLimitExceeded)。客户需要知道检查该特定代码以及是否要处理发送的部分结果。
nil
错误似乎更成功,因此您需要在results
消息中包含大小限制。 One suggestion是定义一个proto枚举:
enum extraStatusCode
UNKNOWN = 0; // not set/used
SIZELIMIT = 1;
TIMELIMIT = 2;
并将其包含在您的结果原型消息中:
message Result
repeated Record records = 1; // results (full or partial)
// ...
extraStatusCode = 15; // indicate if partial results
【讨论】:
【参考方案2】:我建议返回非零错误,然后通过特定调用 errors.Is(err, yourPackage.ConnDeadlineExceeded) 提取错误检查。例如,这种检查使用 GORM (https://gorm.io/docs/error_handling.html#ErrRecordNotFound) 来验证错误是来自数据库的“非犯罪”零结果响应还是需要调用代码执行更多操作的严重错误。
【讨论】:
gRPC 仅提供有限(和数字)的错误代码集 - 因此无法通过错误值返回丰富的错误:github.com/grpc/grpc/blob/master/doc/statuscodes.md 此外,这个解决方案还有很多工作要做,因为我必须改变每个下游服务当前检查错误的方式。以上是关于使用 gRPC,我应该如何处理服务器和客户端之间的部分结果?的主要内容,如果未能解决你的问题,请参考以下文章
grpc-go源码剖析五十九之客户端一侧,是如何处理截止时间呢?