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 也非常简单,只需要调用封装好的 SetHeader 或 SendHeader 方法即可:
// 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,光学字符识别)时,利用传统的图像处理方法进行字符切分仍然是一种有效的途径。即便当前计算机视觉领域主导的是卷积神经网络…...
景联文科技入选量子位智库《中国AIGC数据标注产业全景报告》数据标注行业代表机构
量子位智库《中国AIGC数据标注产业全景报告》中指出,数据标注处于重新洗牌时期,更高质量、专业化的数据标注成为刚需。未来五年,国内AI基础数据服务将达到百亿规模,年复合增长率在27%左右。 基于数据基础设施建设、大模型/AI技术理…...
ClickHouse SQL操作
基本上来说传统关系型数据库(以MySQL为例)的SQL语句,ClickHouse基本都支持,这里不会从头讲解SQL语法只介绍ClickHouse与标准SQL(MySQL)不一致的地方。 1 Insert 基本与标准SQL(MySQL)…...
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行࿰…...
【星海出品】云存储 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…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...
