• Go gRPC 入门


    传送门:

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

    对原文的一些补充:

    1. gRPC中的错误处理

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

    常规用法:

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

    进阶用法:

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

    服务端示例: 

    1. // 生成一个 status.Status
    2. st := status.New(codes.ResourceExhausted, "Request limit exceeded.")
    3. // 填充错误的补充信息 WithDetails
    4. ds, err := st.WithDetails(
    5. &pb.CustomError{ // CustomError 需要在对应的 proto 文件中定义成 message
    6. xxx: "xxx",
    7. xxx: "xxx",
    8. ......
    9. },
    10. )
    11. if err != nil {
    12. return nil, st.Err()
    13. }
    14. return nil, ds.Err()

    客户端示例:

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

    原理:

    这个错误是如何传递给调用方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

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

    客户端发起/接收metadata

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

    服务端发起/接收metadata

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

     

  • 相关阅读:
    数字图像处理实验(一)|图像的基本操作和基本统计指标计算
    12个C语言必背实例
    BUUCTF msic 专题(115)[安洵杯 2019]easy misc
    C Primer Plus(6) 中文版 第8章 字符输入/输出和输入验证 8.6 输入验证
    零基础入门JavaWeb——正则表达式
    用卷积网络对城市住区进行分类
    微信小程序 | IM交友聊天功能大汇总
    5、【办公自动化】Python实现PDF转Word
    ESP32(VSCode+PlatformIO)开发环境搭建教程(2024版)
    【Android进阶】14、顶部应用栏
  • 原文地址:https://blog.csdn.net/hjxisking/article/details/128116347