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

gRPC之metadata

1、metadata

服务间使用 Http 相互调用时,经常会设置一些业务自定义 header 如时间戳、trace信息等,gRPC使用 HTTP/2

协议自然也是支持的,gRPC 通过 google.golang.org/grpc/metadata 包内的 MD 类型提供相关的功能接口。

1.1 类型定义

// MD is a mapping from metadata keys to values. Users should use the following
// two convenience functions New and Pairs to generate MD.
type MD map[string][]string

metadata.MD 类型的定义非常简单,可以像一个普通的 map 一样直接操作,同时 metadata 包里封装了很多工

具方法供我们使用。

// 使用 New 方法创建
md := metadata.New(map[string]string{"k1":"v1", "k2", "v2"})// 直接使用 make 创建
md := make(metadata.MD)// 使用 Pairs 方法创建
md := metadata.Pairs("k1", "v1-1", "k1", "v1-2")// 一些操作
md.Set("key", "v1", "v2")
md.Append("key", "v3")
md.Delete("key")
vals := md.Get("key")

1.2 发送与接收

1.2.1 客户端

客户端请求的 metadata 是通过设置 context 使用的,metadata 包提供了两个 context 相关的方法,设置好

context 后直接在调用 rpc 方法时传入即可:

md := metadata.New(map[string]string{"k1":"v1", "k2", "v2"})// 使用 NewOutgoingContext 初始化一个新的 context
ctx := metadata.NewOutgoingContext(context.Background(), md)// 使用 AppendToOutgoingContext 向 context 追加 metadata
ctx = metadata.AppendToOutgoingContext(ctx, "k3", "v3")

客户端接收响应中的 metadata 需要区分普通 rpc 和 stream rpc :

// 普通 rpc,使用 grpc.Header 方法包装为 CallOption
var md metadata.MD
res, err := client.Ping(ctx, &pb.PingRequest{Value: "ping"}, grpc.Header(&md))// stream rpc
stream, err := client.MultiPong(context.Background(), &pb.PingRequest{Value: "ping"})
if err != nil {log.Fatal(err)
}// 通过 stream 对象的 Header 方法获取
md, err := stream.Header()
if err != nil {log.Fatal(err)
}
1.2.2 服务端

对应客户端请求的 metadata 是使用 context 设置的,那么服务端在接收时自然也是从 context 中读取,

metadata 包中的 FromIncommingContext 方法就是用来读取 context 中的 metadata数据的:

// unary rpc
func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {// 读取请求metadatamd, ok := metadata.FromIncomingContext(ctx)if ok {log.Printf("Got md: %v", md)}// stream rpc
func (s *PingPongServer) MultiPingPong(stream pb.PingPong_MultiPingPongServer) error {md, ok := metadata.FromIncomingContext(stream.Context())if ok {log.Printf("Got md: %v", md)}

服务端设置响应的 metadata 也非常简单,只需要调用封装好的 SetHeaderSendHeader 方法即可:

// unary rpc
func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {// 读取请求metadatamd, ok := metadata.FromIncomingContext(ctx)if ok {log.Printf("Got md: %v", md)}// SetHeader设置响应 metadatagrpc.SetHeader(ctx, metadata.New(map[string]string{"rkey": "rval"}))// 注意 SendHeader 只能调用一次// grpc.SendHeader(ctx, metadata.New(map[string]string{"rkey": "rval"}))// stream rpc, 调用 stream 的 SetHeader 方法
func (s *PingPongServer) MultiPong(req *pb.PingRequest, stream pb.PingPong_MultiPongServer) error {stream.SetHeader(metadata.New(map[string]string{"rkey": "rval"}))// 注意 SendHeader 只能调用一次// stream.SendHeader(metadata.New(map[string]string{"rkey": "rval"}))

1.3 使用案例

1.3.1 proto 文件编写和编译

demo.proto文件内容:

syntax="proto3";
package protos;
option go_package = "./protos;protos";service Greeter {rpc SayHello(HelloRequest) returns(HelloReply){}
}message HelloRequest {string name = 1;
}message HelloReply {string message = 1;
}

编译生成demo.pb.go 文件:

$ protoc --go_out=plugins=grpc:. demo.proto
1.3.2 server端

server.go文件的内容:

package mainimport ("context""flag""fmt""demo/protos""google.golang.org/grpc""google.golang.org/grpc/metadata""log""net"
)var host = "127.0.0.1"var (ServiceName = flag.String("ServiceName", "hello_service", "service name")Port        = flag.Int("Port", 50001, "listening port")
)type server struct {
}func main() {flag.Parse()lis, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", *Port))if err != nil {log.Fatalf("failed to listen:%s", err)} else {fmt.Printf("listen at :%d\n", *Port)}defer lis.Close()s := grpc.NewServer()defer s.GracefulStop()protos.RegisterGreeterServer(s, &server{})addr := fmt.Sprintf("%s:%d", host, *Port)fmt.Printf("server add:%s\n", addr)if err := s.Serve(lis); err != nil {fmt.Printf("failed to server: %s", err)}
}func (s *server) SayHello(ctx context.Context, in *protos.HelloRequest) (*protos.HelloReply, error) {md, ok := metadata.FromIncomingContext(ctx)if !ok {fmt.Printf("get metadata error")}else{fmt.Println("get metadata success: ",md)}if t, ok := md["timestamp"]; ok {fmt.Printf("timestamp from metadata:\n")for i, e := range t {fmt.Printf(" %d. %s\n", i, e)}}if t1, ok1 := md["key1"]; ok1 {fmt.Printf("key1 from metadata:\n")for i, e := range t1 {fmt.Printf(" %d . %s\n", i, e)}}if len(md) > 0 {for k, v := range md {fmt.Printf("%v:%v\n", k, v)}}return &protos.HelloReply{Message: "server: " + in.Name}, nil
}
1.3.3 client端

client.go文件的内容:

package mainimport ("context""fmt""demo/protos""google.golang.org/grpc""google.golang.org/grpc/metadata""time"
)const (timestampFormat = time.StampNano
)func main() {conn, err := grpc.Dial("127.0.0.1:50001", grpc.WithInsecure())if err != nil {panic(err)}client := protos.NewGreeterClient(conn)md := metadata.Pairs("timestamp", time.Now().Format(timestampFormat))md = metadata.New(map[string]string{"key1": "val1", "key2": "val2"})ctx := metadata.NewOutgoingContext(context.Background(), md)resp, err := client.SayHello(ctx, &protos.HelloRequest{Name: "Hello"})if err == nil {fmt.Printf("Reply is : %s\n", resp.Message)} else {fmt.Printf("call server error:%s\n", err)}
}
1.3.4 运行
[root@zsx demo]# go run server.go
listen at :50001
server add:127.0.0.1:50001
get metadata success:  map[:authority:[127.0.0.1:50001] content-type:[application/grpc] key1:[val1] key2:[val2] user-agent:[grpc-go/1.53.0]]
key1 from metadata:0 . val1
:authority:[127.0.0.1:50001]
content-type:[application/grpc]
user-agent:[grpc-go/1.53.0]
key2:[val2]
key1:[val1]
[root@zsx demo]# go run client.go
Reply is : server: Hello
# 项目结构
[root@zsx protoc]# tree demo/
demo/
├── client.go
├── demo.proto
├── go.mod
├── go.sum
├── protos
│   └── demo.pb.go
└── server.go1 directory, 6 files

相关文章:

gRPC之metadata

1、metadata 服务间使用 Http 相互调用时,经常会设置一些业务自定义 header 如时间戳、trace信息等,gRPC使用 HTTP/2 协议自然也是支持的,gRPC 通过 google.golang.org/grpc/metadata 包内的 MD 类型提供相关的功能接口。 1.1 类型定义 /…...

【OpenCV实现图像:OpenCV进行OCR字符分割】

文章目录 概要基本概念读入图像图像二值化小结 概要 在处理OCR(Optical Character Recognition,光学字符识别)时,利用传统的图像处理方法进行字符切分仍然是一种有效的途径。即便当前计算机视觉领域主导的是卷积神经网络&#xf…...

景联文科技入选量子位智库《中国AIGC数据标注产业全景报告》数据标注行业代表机构

量子位智库《中国AIGC数据标注产业全景报告》中指出,数据标注处于重新洗牌时期,更高质量、专业化的数据标注成为刚需。未来五年,国内AI基础数据服务将达到百亿规模,年复合增长率在27%左右。 基于数据基础设施建设、大模型/AI技术理…...

ClickHouse SQL操作

基本上来说传统关系型数据库(以MySQL为例)的SQL语句,ClickHouse基本都支持,这里不会从头讲解SQL语法只介绍ClickHouse与标准SQL(MySQL)不一致的地方。 1 Insert 基本与标准SQL(MySQL&#xff09…...

Ubuntu安装Python环境(使用VSCode)

想在Ubuntu上安装Python环境,选择了VSCode,而不想多装Anaconda等环境,最后参考了这篇博客: python入门开发:ubuntu下搭建python开发环境(vscode)...

QTcpSocket发送结构体的做法

作者:朱金灿 来源:clever101的专栏 为什么大多数人学不会人工智能编程?>>> QTcpSocket发送结构体其实很简单:使用QByteArray类对象进行封装发送,示例代码如下: /* 消息结构体 */ struct stMsg {int m_A…...

微服务学习 | Ribbon负载均衡、Nacos注册中心、微服务技术对比

Ribbon负载均衡 负载均衡流程 负载均衡策略 通过定义IRule实现可以修改负载均衡规则,有两种方式: 1. 代码方式:在服务消费者order-service中的OrderApplication类中,定义一个新的IRule: 2.配置文件方式: 在order-service的application.yml…...

【FPGA】zynq 单端口RAM 双端口RAM 读写冲突 写写冲突

RAMRAM读写分类RAM原理及实现RAM三种读写模式不变模式写优先读优先 单端口 RAM伪双端口 RAM真双端口 RAM读写冲突和写写冲突读写冲突写写冲突总结: RAM RAM 的英文全称是 Random Access Memory,即随机存取存储器,简称随机存储器,…...

【备忘】websocket学习之挖坑埋自己

背景故事 以前没有好好学习过websocket,只知道它有什么用途,也知道是个好东西,平时在工作中没用过,所以对它并不知所以然。如今要做个自己的项目,要在付款的时候实时播报声音。自己是个开发者,也不想用别人…...

大数据研发工程师面试

文章目录 面试1.AUC,ROC,准确率与召回率都是怎么计算的?2.数据清洗是如何清洗的,要做哪些清洗的工作?3.什么是数据的完整性?4.数仓是怎么设计的?5.linux查看进程的命令是什么,如何查看具体某一行的内容(查看第n至m行&#xff0…...

【星海出品】云存储 ceph

https://ceph.com/en/ ceph组件介绍 Monitor 一个Ceph集群需要多个Monitor组成的小集群,它们通过Paxos同步数据,用来保存OSD的元数据。 OSD OSD全称Object Storage Device,也就是负责响应客户端请求返回具体数据的进程。一个Ceph集群一般都有…...

[nlp] grad norm先降后升再降

grad norm先降后升再降正常嘛 在深度学习中,梯度的范数通常被用来衡量模型参数的更新程度,也就是模型的学习进度。在训练初期,由于模型参数的初始值比较随机,梯度的范数可能会比较大,这是正常现象。随着模型的训练&…...

云积天赫AI全域营销系统,为品牌营销注入新活力

AIGC(生成式人工智能)的出现,标志着人工智能已经进入了一个全新的时代,它与传统的人工智能不同,可以更好地理解品牌的需求,并提供更精准的答案。目前,AIGC已经深入到各个领域,其中营…...

Arthas在线修改Java代码

Arthas在线修改Java代码 jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.javamc /tmp/UserController.java -d /tmpretransform /tmp/com/example/demo/arthas/user/UserController.class参考链接: arthas retransform...

mapbox支持的坐标系

mapbox 中只支持 web墨卡托坐标系,不支持经纬度坐标系。 栅格数据 基于经纬度坐标系的栅格数据没有办法渲染。矢量数据 矢量数据代码中会自动转换成墨卡托投影坐标系再渲染。 输出坐标时候还是经纬度。...

腾讯云新客户优惠服务器88元/年,540元/3年,另有5年优惠服务器

在选择云服务器时,首先需要考虑的是性能与配置是否与自己的需求相匹配。对于小型网站或者个人博客,轻量应用服务器是一个不错的选择。腾讯云双十一活动中,2核2G轻量应用服务器的活动优惠价为88元/年,2核4G轻量应用服务器的活动优惠…...

伦敦银和美白银的关系

与黄金相似,世界上白银交易的基础就是伦敦白银市场,人们利用设立在伦敦的专们负责清算银行(与黄金的清算银行相同)所开设的账户进行白银保证金交易。在伦敦市场,以美元清算的伦敦白银价格,是以美元买进1金衡…...

Matplotlib的使用方法

Matplotlib是Python最著名的绘图库,它提供了一整套和Matlab相似的命令API,十分适合交互式地进行制图。而且也可以方便地将它作为绘图控件,嵌入到GUI应用程序中。Matplotlib能够创建多数类型的图表,如条形图、散点图、条形图、饼图…...

【入门篇】1.7 Redis 之 codis 入门介绍

文章目录 1. 简介2. Codis的安装与配置下载编译源码安装1. 安装 Go 运行环境2. 设置编译环境3. 下载 Codis 源代码4. 编译 Codis 源代码 Docker 部署 3. Codis的架构Codis的架构图和组件Codis的工作流程 4. Codis的核心特性自动数据分片数据迁移高可用性全面支持Redis命令分布式…...

【JavaEE】Servlet API 详解(HttpServlet类)

一、HttpServlet 写 Servlet 代码的时候, 首先第一步就是先创建类, 继承自HttpServlet, 并重写其中的某些方法 1.1 HttpServlet核心方法 1.2 Servlet生命周期 这些方法的调用时机, 就称为 “Servlet 生命周期”. (也就是描述了一个 Servlet 实例从生到死的过程) 1.3 处理G…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

<6>-MySQL表的增删查改

目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表&#xf…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...

【HTTP三个基础问题】

面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

网站指纹识别

网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

省略号和可变参数模板

本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...