gRPC 高级——异常处理

gRPC状态码

这一小节我们将介绍一下gRPC内置的状态码,详细请见官方文档

当然在我们自己的项目中,也可以自定义状态码,不过需要在多端维护。

gRPC 使用一组明确定义的状态代码作为 RPC API 的一部分。这些状态定义如下:

CodeNumberDescription
OK0不是错误;成功返回。
CANCELLED1操作通常被调用者取消。
UNKNOWN2未知错误。例如,当从另一个地址空间接收到的 Status 值属于该地址空间中未知的错误空间时,可能会返回此错误。此外,由未返回足够错误信息的 API 引发的错误也可能会转换为此错误。
INVALID_ARGUMENT3客户端指定了无效参数。请注意,这与 FAILED_PRECONDITION 不同。 INVALID_ARGUMENT 表示无论系统状态如何都有问题的参数(例如,格式错误的文件名)。
DEADLINE_EXCEEDED4操作完成之前截止日期已过。对于更改系统状态的操作,即使操作已成功完成,也可能会返回此错误。例如,服务器的成功响应可能会延迟很长时间
NOT_FOUND5找不到某些请求的实体(例如文件或目录)。服务器开发人员请注意:如果整个类别的用户的请求被拒绝,例如逐步推出功能或未记录的许可名单,则可以使用 NOT_FOUND 。如果一类用户中的某些用户的请求被拒绝,例如基于用户的访问控制,则必须使用 PERMISSION_DENIED
ALREADY_EXISTS6客户端尝试创建的实体(例如文件或目录)已存在。
PERMISSION_DENIED 没有权限7调用者没有执行指定操作的权限。 PERMISSION_DENIED 不得用于因耗尽某些资源而导致的拒绝(对于这些错误,请使用 RESOURCE_EXHAUSTED 代替)。如果无法识别调用者,则不得使用 PERMISSION_DENIED (对于这些错误,请使用 UNAUTHENTICATED 代替)。此错误代码并不意味着请求有效或请求的实体存在或满足其他先决条件。
RESOURCE_EXHAUSTED8某些资源已耗尽,可能是每个用户的配额,或者可能是整个文件系统空间不足。
FAILED_PRECONDITION9操作被拒绝,因为系统未处于操作执行所需的状态。例如,要删除的目录非空、对非目录应用 rmdir 操作等。服务实现者可以使用以下准则来决定 FAILED_PRECONDITIONABORTEDUNAVAILABLE :(a) 如果客户端可以仅重试失败的调用,则使用 UNAVAILABLE 。 (b) 如果客户端应在更高级别重试(例如,当客户端指定的测试和设置失败时,指示客户端应重新启动读取-修改-写入序列),请使用 ABORTED 。 © 如果客户端在系统状态明确修复之前不应重试,请使用 FAILED_PRECONDITION 。例如,如果“rmdir”由于目录非空而失败,则应返回 FAILED_PRECONDITION ,因为除非从目录中删除文件,否则客户端不应重试。
ABORTED10操作被中止,通常是由于并发问题,例如定序器检查失败或事务中止。请参阅上面的指南来决定 FAILED_PRECONDITIONABORTEDUNAVAILABLE
OUT_OF_RANGE11尝试的操作超出了有效范围。例如,查找或读取文件末尾之后的内容。与 INVALID_ARGUMENT 不同,此错误指示如果系统状态发生变化则可能会修复的问题。例如,如果要求读取不在 [0,2^32-1] 范围内的偏移量,32 位文件系统将生成 INVALID_ARGUMENT ,但它会生成 OUT_OF_RANGEOUT_OF_RANGE 之间有相当多的重叠。我们建议在应用时使用 OUT_OF_RANGE (更具体的错误),以便迭代空间的调用者可以轻松查找 OUT_OF_RANGE 错误来检测它们何时完成。
UNIMPLEMENTED12此服务未实现或不支持/启用该操作。
INTERNAL13内部错误。这意味着底层系统所期望的一些不变量已经被打破。该错误代码是为严重错误保留的。
UNAVAILABLE14该服务目前不可用。这很可能是一种瞬态情况,可以通过后退重试来纠正。请注意,重试非幂等操作并不总是安全的。
DATA_LOSS15不可恢复的数据丢失或损坏。
UNAUTHENTICATED16该请求没有用于该操作的有效身份验证凭据。

在客户端启动的所有 RPC 都会返回一个由整数 code 和字符串 message 组成的 status 对象。服务器端可以选择为给定 RPC 返回的状态。应用程序应仅使用上面定义的值。遇到超出此范围的值的 gRPC 库必须直接传播它们或将它们转换为 UNKNOWN。

当发生错误时,gRPC 客户端和服务器端实现也可能自行生成并返回 status 。 gRPC 库仅生成预定义状态代码的子集。这允许应用程序确保它看到的任何其他代码实际上是由应用程序返回的(尽管服务器端也可以返回 gRPC 库生成的代码之一)。

下表列出了 gRPC 库(在客户端或服务器端)可能返回的代码,并总结了它们的生成情况。

CaseCodeGenerated at Client or Server
客户端应用程序取消了请求CANCELLEDBoth
服务器返回状态之前截止日期已过DEADLINE_EXCEEDEDBoth
在服务器上找不到方法UNIMPLEMENTEDServer
服务器关闭UNAVAILABLEServer
服务器端应用程序抛出异常(或者执行除返回状态代码之外的其他操作来终止 RPC)UNKNOWNServer
截止日期到期前未收到回复。当客户端无法向服务器发送请求或服务器未能及时响应时,可能会发生这种情况。DEADLINE_EXCEEDEDBoth
连接中断之前传输的一些数据(例如,写入 TCP 连接的请求元数据)UNAVAILABLEClient
无法解压,但支持压缩算法(客户端 -> 服务器)INTERNALServer
无法解压,但支持压缩算法(服务器 -> 客户端)INTERNALClient
服务器不支持客户端使用的压缩机制UNIMPLEMENTEDServer
服务器暂时缺少资源(例如,达到流量控制资源限制)RESOURCE_EXHAUSTEDServer
客户端没有足够的内存来保存服务器响应RESOURCE_EXHAUSTEDClient
违反流量控制协议INTERNALBoth
解析返回状态时出错UNKNOWNClient
身份验证元数据不正确(凭据无法获取元数据、通道和调用上设置的凭据不兼容、 :authority 元数据中设置的主机无效等)UNAUTHENTICATEDBoth
请求基数违规(方法只需要一个请求,但客户端发送了其他数量的请求)UNIMPLEMENTEDServer
响应基数违规(方法只需要一个响应,但服务器发送了其他数量的响应)UNIMPLEMENTEDClient
解析响应原型时出错INTERNALClient
解析请求原型时出错INTERNALServer
发送或接收的消息大于配置的限制RESOURCE_EXHAUSTEDBoth
看门狗超时UNAVAILABLEBoth

gRPC库永远不会生成以下状态代码:

  • INVALID_ARGUMENT
  • NOT_FOUND
  • ALREADY_EXISTS
  • FAILED_PRECONDITION
  • ABORTED
  • OUT_OF_RANGE
  • DATA_LOSS

可能希望重试失败的 RPC 的应用程序必须决定重试哪些状态代码。如上表所示,gRPC 库可以针对不同情况生成相同的状态代码。服务器应用程序也可以返回相同的状态代码。因此,不存在适合在所有应用程序中重试的固定状态代码列表。因此,各个应用程序必须自行确定哪些状态代码应导致 RPC 重试。

gRPC错误处理

这一小节的代码示例我们还是按照gRPC入门的代码作为示例:

0.目录树如下
.
├── client
│   └── client.go
├── proto
│   ├── helloworld_grpc.pb.go
│   ├── helloworld.pb.go
│   └── helloworld.proto
└── server
    └── server.go

1. 编写proto/helloworld.proto代码
syntax = "proto3";

option go_package = ".;proto";

service Hello {
  rpc Hello(HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string name = 1;
}

message HelloResponse {
  string response = 1;
}

利用命令生成对应的go文件

cd proto/
protoc helloworld.proto --go_out=. --go-grpc_out=.
2. 编写server/server.go代码
package main

import (
	"context"
	"google.golang.org/grpc"
	"net"

	"go-try/grpc_error/proto"
)

type HelloServer struct {
	proto.UnimplementedHelloServer
}

func (s *HelloServer) Hello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
	return &proto.HelloResponse{
		Response: "Hello " + request.Name,
	}, nil
}

func main() {
	server := grpc.NewServer()
	proto.RegisterHelloServer(server, &HelloServer{})
	listen, err := net.Listen("tcp", "0.0.0.0:8080")
	if err != nil {
		panic("faild to listen : " + err.Error())
	}
	err = server.Serve(listen)
	if err != nil {
		panic("faild to start grpc : " + err.Error())
	}
}

3.编写client/client.go代码
package main

import (
	"context"
	"fmt"
	"go-try/grpc_error/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/status"
)

func main() {
	conn, err := grpc.NewClient("0.0.0.0:8080", grpc.WithInsecure())
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	cli := proto.NewHelloClient(conn)
	msg, err := cli.Hello(context.Background(), &proto.HelloRequest{Name: "张三"})
	if err != nil {
		st, ok := status.FromError(err)
		if !ok {
			panic("解析失败")
		}
		fmt.Println("code: ", st.Code(), " message: ", st.Message())
	}
	fmt.Println(msg)
}

4.分别运行server.go和client.go

运行结果如下:

response:"Hello 张三"

这是运行成功的示例。但是我们需要了解如何进行错误处理,于是我们修改server端代码。

5. 修改server/server.go代码

我们将Hello()函数修改

func (s *HelloServer) Hello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
	return nil, status.Error(codes.NotFound, "数据库中不存在 "+request.Name)
}

再次分别运行分别运行server.go和client.go,

运行结果如下:

code:  NotFound , message:  数据库中不存在 张三

这说明我们已经成功完成了这个错误处理的小demo。相信你已经学会了如何使用gRPC错误处理。

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用 gRPC Java 进行通信时,我们需要考虑异常处理以确保我们的应用程序具有适当的容错能力。下面是一些常见的 gRPC 异常及其处理方法: 1. RpcException:这是一个通用的 gRPC 异常,表示 RPC 调用过程中出现了错误。可以通过捕获 RpcException 来处理 gRPC 的任何异常。例如: ```java try { // Make a gRPC call } catch (StatusRuntimeException e) { // Handle the exception } ``` 2. StatusRuntimeException:这是一个特殊的 RpcException 子类,表示 RPC 调用失败并返回了一个错误状态码。可以通过 getStatus() 方法来获取状态码并进行相应的处理。例如: ```java try { // Make a gRPC call } catch (StatusRuntimeException e) { if (e.getStatus().getCode() == Status.Code.NOT_FOUND) { // Handle the "not found" error } else if (e.getStatus().getCode() == Status.Code.PERMISSION_DENIED) { // Handle the "permission denied" error } else { // Handle any other errors } } ``` 3. DeadlineExceededException:这是一个表示 RPC 超时的异常。可以通过捕获 DeadlineExceededException 来处理超时异常。例如: ```java try { // Make a gRPC call } catch (DeadlineExceededException e) { // Handle the timeout error } ``` 4. CancellationException:这个异常表示 RPC 调用已被取消。可以通过捕获 CancellationException 来处理取消异常。例如: ```java try { // Make a gRPC call } catch (CancellationException e) { // Handle the cancellation error } ``` 以上是一些常见的 gRPC 异常及其处理方法,当然还有其他更详细的异常类型,可以在 gRPC Java 的官方文档中找到。在编写 gRPC 应用程序时,我们应该根据实际情况选择合适的异常类型并进行适当的处理

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值