0.1、索引
https://waterflow.link/articles/1665938704477
我们都知道当发起http请求的时候,服务端会返回一些http状态码,不管是成功还是失败。客户端可以根据服务端返回的状态码,判断服务器出现了哪些错误。
我们经常用到的比如下面这些:
- 200:OK,请求成功
- 204:NO CONTENT,此请求没有要发送的内容,但标头可能很有用。 用户代理可以用新的更新其缓存的资源头。
- 400:Bad Request,由于被认为是客户端错误(例如,格式错误的请求语法、无效的请求消息帧或欺骗性请求路由),服务器无法或不会处理请求。
- 404:Not Found,服务器找不到请求的资源。 在浏览器中,这意味着无法识别 URL。 在 API 中,这也可能意味着端点有效但资源本身不存在。
同样的,当我们调用 gRPC 调用时,客户端会收到带有成功状态的响应或带有相应错误状态的错误。 客户端应用程序需要以能够处理所有潜在错误和错误条件的方式编写。 服务器应用程序要求您处理错误并生成具有相应状态代码的适当错误。
发生错误时,gRPC 会返回其错误状态代码之一以及可选的错误消息,该消息提供错误条件的更多详细信息。 状态对象由一个整数代码和一个字符串消息组成,这些消息对于不同语言的所有 gRPC 实现都是通用的。
gRPC 使用一组定义明确的 gRPC 特定状态代码。 这包括如下状态代码:
- OK:成功状态,不是错误。
- CANCELLED:操作被取消,通常是由调用者取消的。
- DEADLINE_EXCEEDED:截止日期在操作完成之前到期。
- INVALID_ARGUMENT:客户端指定了无效参数。
详细的状态code、number和解释可以参考这里:https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
1、grpc错误
之前的章节中我们写过关于简单搭建grpc的文章:https://waterflow.link/articles/1665674508275
我们在这个基础上稍微修改一下,看下下面的例子。
首先我们在服务端,修改下代码,在service的Hello方法中加个判断,如果客户端传过来的不是hello,我们我们将返回grpc的标准错误。像下面这样:
func (h HelloService) Hello(ctx context.Context, args *String) (*String, error) {
time.Sleep(time.Second)
// 返回参数不合法的错误
if args.GetValue() != "hello" {
return nil, status.Error(codes.InvalidArgument, "请求参数错误")
}
reply := &String{Value: "hello:" + args.GetValue()}
return reply, nil
}
我们客户端的代码像下面这样:
func unaryRpc(conn *grpc.ClientConn) {
client := helloservice.NewHelloServiceClient(conn)
ctx := context.Background()
md := metadata.Pairs("authorization", "mytoken")
ctx = metadata.NewOutgoingContext(ctx, md)
// 调用Hello方法,并传入字符串hello
reply, err := client.Hello(ctx, &helloservice.String{Value: "hello"})
if err != nil {
log.Fatal(err)
}
log.Println("unaryRpc recv: ", reply.Value)
}
我们开启下服务端,并运行客户端代码:
go run helloclient/main.go
invoker request time duration: 1
2022/10/16 23:05:18 unaryRpc recv: hello:hello
可以看到会输出正确的结果。现在我们修改下客户端代码:
func unaryRpc(conn *grpc.ClientConn) {
client := helloservice.NewHelloServiceClient(conn)
ctx := context.Background()
md := metadata.Pairs("authorization", "mytoken")
ctx = metadata.NewOutgoingContext(ctx, md)
// 调用Hello方法,并传入字符串f**k
reply, err := client.Hello(ctx, &helloservice.String{Value: "f**k"})
if err != nil {
log.Fatal(err)
}
log.Println("unaryRpc recv: ", reply.Value)
}
然后运行下客户端代码:
go run helloclient/main.go
invoker request time duration: 1
2022/10/16 23:14:13 rpc error: code = InvalidArgument desc = 请求参数错误
exit status 1
可以看到我们获取到了服务端返回的错误。
2、获取grpc错误类型
有时候客户端通过服务端返回的不同错误类型去做一些具体的处理,这个时候客户端可以这么写:
func unaryRpc(conn *grpc.ClientConn) {
client := helloservice.NewHelloServiceClient(conn)
ctx := context.Background()
md := metadata.Pairs("authorization", "mytoken")
ctx = metadata.NewOutgoingContext(ctx, md)
reply, err := client.Hello(ctx, &helloservice.String{Value: "f**k"})
if err != nil {
fromError, ok := status.FromError(err)
if !ok {
log.Fatal(err)
}
// 判断服务端返回的是否是指定code的错误
if fromError.Code() == codes.InvalidArgument {
log.Fatal("invalid arguments")
}
}
log.Println("unaryRpc recv: