gRPC 中的错误处理:构建更健壮、更可靠的微服务!

开发 前端
gRPC 提供了灵活的错误处理机制,允许你以结构化的方式传递错误信息,帮助你构建更健壮、更可靠的微服务。通过正确使用 status.Status​ 和 status.WithDetails 方法,你可以确保你的错误信息清晰易懂,并能被客户端轻松理解和处理。

gRPC 在设计上鼓励将错误处理作为服务的一部分,而不是将其隐藏在消息体中。每个 gRPC 服务天生就拥有一个错误返回值,作为专门的错误传输通道。所有 gRPC 中的错误返回值应该要么是 nil,要么是 由 status.Status 生成的错误。这样可以确保调用方可以轻松识别错误。

1. 基本用法

简单地返回 Go 错误并不能被下游客户端识别。正确的做法是:

  • 调用 status.New 方法并传入合适的错误代码以生成 status.Status 对象。
  • 调用 status.Err 方法生成调用方可以识别的错误,然后返回。
st := status.New(codes.NotFound, "some description")  
err := st.Err()

传入的错误代码类型为 codes.Code。或者,你可以使用 status.Error 方法,它可以避免手动转换。

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

2. 高级用法

上述错误有一个限制:codes.Code 定义的错误代码只涵盖了某些场景,无法全面表达业务中遇到的各种错误场景。

gRPC 提供了一种机制来补充错误中的信息:status.WithDetails 方法。

客户端可以通过将错误转换回 status.Status 并使用 status.Details 方法直接获取内容。

status.Details 返回一个切片,它是 interface{} 的切片。但是,Go 会自动执行类型转换,允许通过断言直接使用。

服务器端示例

  • 生成 status.Status 对象
  • 填充额外的错误信息
func ErrorWithDetails() error {  
    st := status.Newf(codes.Internal, fmt.Sprintf("something went wrong: %v", "api.Getter"))  
    v := &errdetails.PreconditionFailure_Violation{ //errDetails  
       Type:        "test",  
       Subject:     "12",  
       Description: "32",  
    }  
    br := &errdetails.PreconditionFailure{}  
    br.Violations = append(br.Violations, v)  
    st, _ = st.WithDetails(br)  
    return st.Err()  
}

客户端端示例

  • 在 RPC 错误后解析错误信息
  • 通过断言直接获取错误详细信息
resp, err := odinApp.CreatePlan(cli.StaffId.AssetId, gentRatePlanMeta(cli.StaffId))  
  
  if status.Code(err) != codes.InvalidArgument {  
    logger.Error("create plan error:%v", err)  
  } else {  
    for _, d := range status.Convert(err).Details() {  
      //   
      switch info := d.(type) {  
      case *errdetails.QuotaFailure:  
        logger.Info("Quota failure: %s", info)  
      case *errdetails.PreconditionFailure:  
        detail := d.(*errdetails.PreconditionFailure).Violations  
        for _, v1 := range detail {  
          logger.Info(fmt.Sprintf("details: %+v", v1))  
        }  
      case *errdetails.ResourceInfo:  
        logger.Info("ResourceInfo: %s", info)  
  
      case *errdetails.BadRequest:  
        logger.Info("ResourceInfo: %s", info)  
  
      default:  
        logger.Info("Unexpected type: %s", info)  
      }  
    }  
  }  
  logger.Infof("create plan success,resp=%v", resp)

原理

这些错误是如何传递给调用方客户端的呢?它们被放置在元数据中,然后在 HTTP 头部中。元数据以键值对的形式存在。在错误传输中,键是一个固定值:grpc-status-details-bin。值由 proto 编码,并且是二进制安全的。大多数语言都实现了这种机制。

图片图片

注意

gRPC 对响应头有限制,最大为 8K,因此错误不能太大。

参考

  • Protocol Buffers Tutorial[1]
  • errdetails[2]

总结

gRPC 提供了灵活的错误处理机制,允许你以结构化的方式传递错误信息,帮助你构建更健壮、更可靠的微服务。通过正确使用 status.Status 和 status.WithDetails 方法,你可以确保你的错误信息清晰易懂,并能被客户端轻松理解和处理。

参考资料

[1] Protocol Buffers Tutorial: https://protobuf.dev/getting-started/gotutorial/

[2] errdetails: https://pkg.go.dev/google.golang.org/genproto/googleapis/rpc/errdetails

责任编辑:武晓燕 来源: 源自开发者
相关推荐

2015-05-04 14:12:43

2018-12-03 08:00:00

微服务gRPC

2022-10-17 00:14:55

微服务税mock代理服务

2013-05-07 10:11:28

2018-05-14 17:16:57

BSI云计算标准

2023-12-26 22:05:53

并发代码goroutines

2021-04-14 07:08:14

Nodejs错误处理

2016-09-07 20:28:17

MySQL存储数据库

2023-08-31 08:50:46

Scrapy网络爬取

2023-10-28 16:30:19

Golang开发

2022-06-07 08:19:30

gRPCBallerina微服务

2021-04-29 09:02:44

语言Go 处理

2023-10-26 12:05:14

Golang开发

2014-11-17 10:05:12

Go语言

2024-09-23 08:10:00

.NET开发

2023-09-19 23:30:25

单元测试代码

2021-08-11 13:54:19

微服务系统架构开发者

2020-04-26 19:12:29

shell脚本Linux

2024-03-27 08:18:02

Spring映射HTML

2022-05-06 08:00:51

Golang编程语言Java
点赞
收藏

51CTO技术栈公众号