错误处理

gRPC code

类似于HTTP定义了一套响应状态码,gRPC也定义有一些状态码。Go语言中此状态码由codes定义,本质上是一个uint32。

1
type Code uint32

使用时需导入google.golang.org/grpc/codes包。

1
import "google.golang.org/grpc/codes"

目前已经定义的状态码有如下几种。

Code 含义
OK 0 请求成功
Canceled 1 操作已取消
Unknown 2 未知错误。如果从另一个地址空间接收到的状态值属 于在该地址空间中未知的错误空间,则可以返回此错误的示例。 没有返回足够的错误信息的API引发的错误也可能会转换为此错误
InvalidArgument 3 表示客户端指定的参数无效。 请注意,这与 FailedPrecondition 不同。 它表示无论系统状态如何都有问题的参数(例如,格式错误的文件名)。
DeadlineExceeded 4 表示操作在完成之前已过期。对于改变系统状态的操作,即使操作成功完成,也可能会返回此错误。 例如,来自服务器的成功响应可能已延迟足够长的时间以使截止日期到期。
NotFound 5 表示未找到某些请求的实体(例如,文件或目录)。
AlreadyExists 6 创建实体的尝试失败,因为实体已经存在。
PermissionDenied 7 表示调用者没有权限执行指定的操作。 它不能用于拒绝由耗尽某些资源引起的(使用 ResourceExhausted )。 如果无法识别调用者,也不能使用它(使用 Unauthenticated )。
ResourceExhausted 8 表示某些资源已耗尽,可能是每个用户的配额,或者整个文件系统空间不足
FailedPrecondition 9 指示操作被拒绝,因为系统未处于操作执行所需的状态。 例如,要删除的目录可能是非空的,rmdir 操作应用于非目录等。
Aborted 10 表示操作被中止,通常是由于并发问题,如排序器检查失败、事务中止等。
OutOfRange 11 表示尝试超出有效范围的操作。
Unimplemented 12 表示此服务中未实施或不支持/启用操作。
Internal 13 意味着底层系统预期的一些不变量已被破坏。 如果你看到这个错误,则说明问题很严重。
Unavailable 14 表示服务当前不可用。这很可能是暂时的情况,可以通过回退重试来纠正。 请注意,重试非幂等操作并不总是安全的。
DataLoss 15 表示不可恢复的数据丢失或损坏
Unauthenticated 16 表示请求没有用于操作的有效身份验证凭据
_maxCode 17 -

gRPC Status

Go语言使用的gRPC Status 定义在google.golang.org/grpc/status,使用时需导入。

1
import "google.golang.org/grpc/status"

RPC服务的方法应该返回 nil 或来自status.Status类型的错误。客户端可以直接访问错误。

创建错误

当遇到错误时,gRPC服务的方法函数应该创建一个 status.Status。通常我们会使用 status.New函数并传入适当的status.Code和错误描述来生成一个status.Status。调用status.Err方法便能将一个status.Status转为error类型。也存在一个简单的status.Error方法直接生成error。下面是两种方式的比较。

1
2
3
4
5
6
7
// 创建status.Status
st := status.New(codes.NotFound, "some description")
err := st.Err() // 转为error类型

// vs.

err := status.Error(codes.NotFound, "some description")

为错误添加其他详细信息

在某些情况下,可能需要为服务器端的特定错误添加详细信息。status.WithDetails就是为此而存在的,它可以添加任意多个proto.Message,我们可以使用google.golang.org/genproto/googleapis/rpc/errdetails中的定义或自定义的错误详情。

1
2
3
4
5
st := status.New(codes.ResourceExhausted, "Request limit exceeded.")
ds, _ := st.WithDetails(
// proto.Message
)
return nil, ds.Err()

然后,客户端可以通过首先将普通error类型转换回status.Status,然后使用status.Details来读取这些详细信息。

1
2
3
4
s := status.Convert(err)
for _, d := range s.Details() {
// ...
}