Go gRPC 入门
创始人
2024-03-03 23:55:44
0

传送门:

Go微服务(三)——gRPC详细入门_小象裤衩的博客-CSDN博客

对原文的一些补充:

1. gRPC中的错误处理

    一般接口都会定义统一的错误返回格式,如果在proto文件中的每个message消息体内硬是增加一个错误消息结构,十分的不优雅,go 的 grpc 包提供了一个 status 功能,可以通过metadata(下面再介绍metadata)在header中返回给客户端,这样就不用修改每个接口的message消息体了

常规用法:

  • 调用 status.New 方法,并传入一个适当的错误码,生成一个 status.Status 对象
  • 调用该 status.Err 方法生成一个能被调用方识别的error,然后返回
st := status.New(codes.NotFound, "some description")
err := st.Err()// 等同于 status.Error(codes.NotFound, "some description")

进阶用法:

status.New只能声明一个错误code(还不能自定义)以及一段msg文本,如果需要更丰富的报错,则需要使用 WithDetails 功能自定义错误结构体

服务端示例: 

// 生成一个 status.Status 
st := status.New(codes.ResourceExhausted, "Request limit exceeded.")
// 填充错误的补充信息 WithDetails
ds, err := st.WithDetails(&pb.CustomError{    // CustomError 需要在对应的 proto 文件中定义成 messagexxx: "xxx",xxx: "xxx",......},
)
if err != nil {return nil, st.Err()
}
return nil, ds.Err()

客户端示例:

r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "world"})
// 调用 RPC 如果遇到错误就对错误处理
if err != nil {// 转换错误s := status.Convert(err)// 解析错误信息for _, d := range s.Details() {// 通过断言直接使用switch info := d.(type) {case *pb.CustomError:log.Printf("Custom error: %s", info)default:log.Printf("Unexpected type: %s", info)}}
}

原理:

这个错误是如何传递给调用方Client的呢?

是放到 metadata中的,而metadata是放到HTTP的header中的。

metadata是key:value格式的数据。错误的传递中,key是个固定值:grpc-status-details-bin。

而value,是被proto编码过的,是二进制安全的。

目前大多数语言都实现了这个机制

2. metadata的使用

metadata,简称MD,一般用来传递消息以外的额外数据(挂载数据):

  • 链路追踪的参数,如traceId spanId等
  • 错误的详情
  • 签名校验
  • 其他通用参数

 gRPC是基于HTTP2的,HTTP2中除了 header 还有 trailer。

metadata就是放在header和trailer中传输的

客户端发起请求的时候可以带,服务端返回数据的时候也可以带。

metadata是一个key-value结构的数据(map),但是value是string类型的切片

type MD map[string][]string

客户端发送请求的MD都会放到header中

服务端响应的MD可以选择放到header,也可以选择放到trailer

对于Unary模式的调用,因为是一次请求一次响应,所以放在哪里无所谓

构造metadata

// key1 的值将会是一个slice,有两个值: []string{"val1", "val1-2"}
md := metadata.Pairs("key1", "val1","key1", "val1-2","key2", "val2",
)

客户端发起/接收metadata

  • 客户端将md放入ctx上下文中发起请求
//方式1:创建一个带md的context
md := metadata.Pairs("k1", "v1", "k1", "v2", "k2", "v3")
ctx := metadata.NewOutgoingContext(context.Background(), md)//方式2:对原有的 context 追加,AppendToOutgoingContext 
ctx := metadata.AppendToOutgoingContext(ctx, "k1", "v1", "k1", "v2", "k2", "v3")//方式3:对原有的 context 追加参数,metadata.Join
send, _ := metadata.FromOutgoingContext(ctx)
newMD := metadata.Pairs("k3", "v3")
ctx = metadata.NewOutgoingContext(ctx, metadata.Join(send, newMD))
  • 客户端接收从服务端得到的md
// 提前声明用于接收的变量
var header, trailer metadata.MD
r, err := client.SomeRPC(ctx,someRequest,grpc.Header(&header),    // 接收的header放在这里grpc.Trailer(&trailer),  // 接收的trailer放这里
)fmt.Println(header.Get("key"))    // 打印从服务端这边得到的md中定义的key

服务端发起/接收metadata

  • 服务端也是从ctx中获取metadata
//unary 模式
md, ok := metadata.FromIncomingContext(ctx)
  • 可以通过header或者trailer发起metadata给客户端
func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {// 创建并设置 headerheader := metadata.Pairs("header-key", "val")grpc.SendHeader(ctx, header)// 创建并设置 trailertrailer := metadata.Pairs("trailer-key", "val")grpc.SetTrailer(ctx, trailer)
}

 

相关内容

热门资讯

美国2年期国债收益率上涨15个... 原标题:美国2年期国债收益率上涨15个基点 美国2年期国债收益率上涨15个基...
汽车油箱结构是什么(汽车油箱结... 本篇文章极速百科给大家谈谈汽车油箱结构是什么,以及汽车油箱结构原理图解对应的知识点,希望对各位有所帮...
嵌入式 ADC使用手册完整版 ... 嵌入式 ADC使用手册完整版 (188977万字)💜&#...
重大消息战皇大厅开挂是真的吗... 您好:战皇大厅这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...
盘点十款牵手跑胡子为什么一直... 您好:牵手跑胡子这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游...
senator香烟多少一盒(s... 今天给各位分享senator香烟多少一盒的知识,其中也会对sevebstars香烟进行解释,如果能碰...
终于懂了新荣耀斗牛真的有挂吗... 您好:新荣耀斗牛这款游戏可以开挂,确实是有挂的,需要了解加客服微信8435338】很多玩家在这款游戏...
盘点十款明星麻将到底有没有挂... 您好:明星麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【5848499】很多玩家在这款游戏...
总结文章“新道游棋牌有透视挂吗... 您好:新道游棋牌这款游戏可以开挂,确实是有挂的,需要了解加客服微信【7682267】很多玩家在这款游...
终于懂了手机麻将到底有没有挂... 您好:手机麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...