当前位置: 首页 > news >正文

认识GO--gRPC的metadata

参考:

写给go开发者的gRPC教程-metadata-CSDN博客icon-default.png?t=O83Ahttps://blog.csdn.net/kevin_tech/article/details/129395177?ops_request_misc=%257B%2522request%255Fid%2522%253A%25221f2f2e26f48c755c33344ccb171a49fc%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=1f2f2e26f48c755c33344ccb171a49fc&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-129395177-null-null.142^v100^pc_search_result_base6&utm_term=go%E7%9A%84metadata&spm=1018.2226.3001.4187

什么是metadata?

元数据。他就是我们的对数据进行封装的一个单位化数据。在某些理解上,他是一个。他们是一个整体。这么说有点抽象,举例说明吧:目前我们有一个请求,请求的数据是:用户名+密码。这就是我们常说的”数据“。那么请求的方法是什么?编码是什么?token呢?cookie呢?他们提供了有关其他数据的信息,用于描述数据的特征、来源、用途、关系等诸多方面。因此,我们将他们也封装到一起,打包成我们的metadata,一起发送,一起接收......

那么在go语言中,metadata又是什么?具体的说,应该是在go的grpc中,请求和响应携带的“元数据”,他们就叫做 metadata。grpc基于http2.0,那么metadata在grpc的一次调用就是一个生命周期。

简单的理解:metadata是context中负责数据打包的载体,配合context实现服务端和客户端之间的互相传递信息。

metadata的创建

我们先了解一个东西:在grpc中,metadata的本质其实是一个map[string][]string。

New()

md := metadata.New(map[string]string{"key1":"value1","key2":"value2"})

Pairs()

要注意如果有相同的 key 会自动合并

md := metadata.Pairs(

         "key1", "value1",

         "key1", "value1.2",  

         "key2", "value2",

)

 "key1" will have map value []string{"value1", "value1.2"}

Join()

md1 :=  metadata.Pairs("k1", "v1", "k2", "v2")
md2 := metadata.New(map[string]string{"key1":"value1","key2":"value2"})
 
md := metadata.Join(md1, md2)

合并md1和md2为md

存储二进制数据(-bin)

在 metadata 中,key 永远是 string 类型,但是 value 可以是 string 也可以是二进制数据。为了在 metadata 中存储二进制数据,我们仅仅需要在 key 的后面加上一个 - bin 后缀。具有 - bin 后缀的 key 所对应的 value 在创建 metadata 时会被编码(base64),收到的时候会被解码:

md := metadata.Pairs(

  "key", "string value",

  "key-bin", string([]byte{96, 102},

)

关于其他metadata的操作:https://pkg.go.dev/google.golang.org/grpc@v1.44.0/metadata

metadata的发送

由于,他是为了配合context传递上下文信息的,所以,metadata的传递可以是双向的。

Client----->Server

Client发送

覆盖式发送NewOutgoingContext

ctx := metadata.NewOutgoingContext(context.Background(), md)

追加式发送AppendToOutgoingContext

// 如果对应的 context 没有 metadata,那么就会创建一个
ctx := metadata.AppendToOutgoingContext(ctx, "k1", "v1", "k1", "v2", "k2", "v3")


// 如果已有 metadata 了,那么就将数据添加到原来的 metadata  (例如在拦截器中)
ctx := metadata.AppendToOutgoingContext(ctx, "k3", "v4")

Server接收

使用FromIncomingContext

这里不管是普通调用还是流式调用,都是从contex.Context中读取metadata

核心的代码:

// 普通调用

md, ok := metadata.FromIncomingContext(ctx)

// 流式调用

md, ok := metadata.FromIncomingContext(stream.Context())

Server----->Client

服务端发送的metadata被分成了header trailer两者,因而客户端也可以读取两者。

trailer:服务端在处理完一个请求后,除了正常的响应消息主体外,可能还需要传递一些不能在响应头部(header)中发送的信息。这些信息可能是在处理请求过程中才生成的,或者因为某些协议规定、性能考虑等因素而适合放在消息结尾发送。

Server发送

对于普通RPC(unary RPC)server可以使用grpc包中提供的函数向client发送 header 和trailer

  • grpc.SendHeader()

  • grpc.SetHeader()

  • grpc.SetTrailer()

对于流式RPC(streaming RPC)server可以使用ServerStream[1]接口中定义的函数向client发送header和 trailer

  • ServerStream.SendHeader()

  • ServerStream.SetHeader()

  • ServerStream.SetTrailer()

其中:

sendHeader是立即发送

setHeader是追加设置header,自动合并到一个header中,等到合适的机会发送

setTrailer一般设置一次,防止信息设置混乱,因此也是 发送 tailer 信息

流式的差不多

Client接收

 普通RPC使用grpc.Header()grpc.Trailer()

流式RPC使用 ClientStream接口的返回对象调用

HeaderTrailer区别

根本区别:发送的时机不同!

✨ headers会在下面三种场景下被发送

  • SendHeader() 被调用时(包含grpc.SendHeaderstream.SendHeader)

  • 第一个响应被发送时

  • RPC结束时(包含成功或失败)

✨ trailer会在rpc返回的时候,即这个请求结束的时候被发送

差异在流式RPC(streaming RPC)中比较明显:

因为trailer是在服务端发送完请求之后才发送的,所以client获取trailer的时候需要在stream.CloseAndRecv或者stream.Recv 返回非nil错误 (包含 io.EOF)之后

如果stream.CloseAndRecv之前调用stream.Trailer()获取的是空

stream, err := client.SomeStreamingRPC(ctx)// retrieve header
header, err := stream.Header()// retrieve trailer 
// `trailer`会在rpc返回的时候,即这个请求结束的时候被发送
// 因此此时调用`stream.Trailer()`获取的是空
trailer := stream.Trailer()stream.CloseAndRecv()// retrieve trailer 
// `trailer`会在rpc返回的时候,即这个请求结束的时候被发送
// 因此此时调用`stream.Trailer()`才可以获取到值
trailer := stream.Trailer()

使用场景

当我们把metadata类比成HTTP Header的时候,那么metadata的使用场景也可以借鉴HTTPHeader。如传递用户token进行用户认证,传递trace进行链路追踪等

http://www.lryc.cn/news/504907.html

相关文章:

  • 2024年安徽省职业院校技能大赛信息安全管理与评估
  • Perl 引用
  • RT-Thread启动过程 :从汇编开始的启动流程
  • Scala—“==“和“equals“用法(附与Java对比)
  • $route和$router的区别
  • [工具升级问题] 钉钉(linux版)升级带来的小麻烦
  • Leetcode经典题13--接雨水
  • yarn修改缓存位置
  • OpenHarmony-3.HDF input子系统(5)
  • RabbitMQ 消息持久化/镜像队列/lazy对时延影响
  • 【深度学习】深刻理解Swin Transformer
  • [2015~2024]SmartMediaKit音视频直播技术演进之路
  • redis 使用Lettuce 当redis挂掉重启之后 网络是怎么重新连接
  • 【IntelliJ IDEA 集成工具】TalkX - AI编程助手
  • 二叉搜索树Ⅲ【东北大学oj数据结构8-3】C++
  • 【面试笔记】CPU 缓存机制
  • MySQL基础函数使用
  • 解决docker环境下aspose-words转换word成pdf后乱码问题
  • C# 生成随机数的方法
  • ip_done
  • 3D可视化引擎HOOPS Visualize与HOOPS Luminate Bridge的功能与应用
  • Docder 搭建Redis分片集群 散片插槽 数据分片 故障转移 Java连接
  • 校园交友app/校园资源共享小程序/校园圈子集合二手物品交易论坛、交友等综合型生活服务社交论坛
  • Chaos Mesh云原生的混沌测试平台搭建
  • Vue3之组合式API详解
  • 大模型的构建与部署(3)——数据标注
  • AI发展与LabVIEW程序员就业
  • 本地事务 + 消息队列事务方案设计
  • pinctrl子系统学习笔记
  • 使用vue-element 的计数器inputNumber,传第三个参数