跟我一起学Go系列:gRPC 全局数据传输和超时处理
gRPC 在多个 GoRoutine 之间传递数据使用的是 Go SDK 提供的 Context 包。关于 Context 的使用可以看我之前的一篇文章:Context 使用。
但是 Context 的使用场景是同一个进程内,gRPC 使用都是跨进程的网络传输,如果在某个调用链上 A 服务当前要调用 B 服务传递一些上下文参数并且也希望 B 服务继续往下传递该如何实现呢?
跨进程的全局数据传输
再次回忆一下 gRPC 是基于 HTTP/2 协议的。那我们是不是可以再请求头中将这一部分数据 set 进去,而不是放在数据包里面。
gRPC 也是如此实现的。进程间传输定义了一个 metadata 对象,该对象放在 Request-Headers 内:
Requests
Request → Request-Headers *Length-Prefixed-Message EOS
Request-Headers are delivered as HTTP2 headers in HEADERS + CONTINUATION frames.
Request-Headers → Call-Definition *Custom-Metadata
Call-Definition → Method Scheme Path TE [Authority] [Timeout] Content-Type [Message-Type] [Message-Encoding] [Message-Accept-Encoding] [User-Agent]
Method → ":method POST"
Scheme → ":scheme " ("http" / "https")
Path → ":path" "/" Service-Name "/" {method name} # But see note below.
Service-Name → {IDL-specific service name}
......
......
......
Custom-Metadata → Binary-Header / ASCII-Header
......
Custom-Metadata 字段内即为我们要传输的全局对象。具体文档可以看这里:PROTOCOL-HTTP2。
所以通过 metadata 我们可以将上一个进程中的全局对象透传到下一个被调用的进程。查看源码可以发现 metadata 内部实际上是通过一个 map 对象存储数据:
type MD map[string][]string
metadata 和 Context 一起连用的使用方式如下:
发送方如果想发送一些全局字段给接收方,首先从自己端的 metadata set 数据:
//set 数据到 metadata
md := metadata.Pairs("key", "val")
// 新建一个有 metadata 的 context
ctx := metadata.NewOutgoingContext(context.Background(), md)
注意上面的 NewOutgoingContext() 方法,命名很形象,向外输出 Context。那么对端接收的时候肯定有一个对应的方法,我们继续往下看。这个新的 Context 就可以用来发送出去,比如还是我们上文中的示例方法:
//set 数据到 metadata
md := metadata.Pairs("key", "val")
// 新建一个有 metadata 的 context
ctx := metadata.NewOutgoingContext(context.Background(), md)
c = NewTokenServiceClient(conn)
hello, err := c.SayHello(ctx, &PingMessage{Greeting: "hahah"})
if err != nil {
fmt.Printf("could not greet: %v", err)
}
对于接收方来说,无非就是解析 metadata 中的数据。gRPC 已经帮我们将数据解析到 context 中,所以需要从 Context 中取出 MD 对象。
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
fmt.Printf("get metadata error")
}
if t, ok := md["key"]; ok {
fmt.Printf("key from metadata:\n")
for i, e := range t {
fmt.Printf(" %d. %s\n", i, e)
}
}
这里取数的逻辑使用了 metadata 的 FromIncomingContext() 方法。跟存数据的 NewOutgoingContext() 方法遥相呼应。
跨进程的超时停止
同进程下跨 Goroutine 我们还是可以使用 Context 来设置当前 Context 管理下子 Goroutine 的有效期:
//超时截止
context.WithTimeout(context.Background(), 100*time.Millisecond)
//限制截止
deadline, c2 := context.WithDeadline(context.Background(), deadline time.Time)
gRPC 中同样实现了这个功能,即跨进程间的 Context 传递实现进程间的 Context 生命周期管理。我们看一个简单的例子:
服务端:
package normal
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
pb "gorm-demo/models/pb"
"net"
"testing"
"time"
)
type server struct{}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
time.Sleep(3 * time.Second)
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
//拦截器 - 打印日志
func LoggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
fmt.Printf("gRPC method: %s, %v", info.FullMethod, req)
resp, err := handler(ctx, req)
fmt.Printf("gRPC method: %s, %v", info.FullMethod, resp)
return resp, err
}
func TestGrpcServer(t *testing.T) {
// 监听本地的8972端口
lis, err := net.Listen("tcp", ":8972")
if err != nil {
fmt.Printf("failed to listen: %v", err)
return
}
//注册拦截器
s := grpc.NewServer(grpc.UnaryInterceptor(LoggingInterceptor)) // 创建gRPC服务器
pb.RegisterGreeterServer(s, &server{}) // 在gRPC服务端注册服务
reflection.Register(s) //在给定的gRPC服务器上注册服务器反射服务
// Serve方法在lis上接受传入连接,为每个连接创建一个ServerTransport和server的goroutine。
// 该goroutine读取gRPC请求,然后调用已注册的处理程序来响应它们。
err = s.Serve(lis)
if err != nil {
fmt.Printf("failed to serve: %v", err)
return
}
}
服务端代码我们在 SayHello() 方法中增加了 3s 的sleep。客户端代码如下:
package normal
import (
"fmt"
"testing"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc"
pb "gorm-demo/models/pb"
)
func TestGrpcClient(t *testing.T) {
// 连接服务器
conn, err := grpc.Dial(":8972", grpc.WithInsecure())
if err != nil {
fmt.Printf("faild to connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
//timeout, cancelFunc := context.WithTimeout(context.Background(), time.Second*2)
//defer cancelFunc()
m, _ := time.ParseDuration("1s")
result := time.Now().Add(m)
deadline, c2 := context.WithDeadline(context.Background(), result)
defer c2()
// 调用服务端的SayHello
r, err := c.SayHello(deadline, &pb.HelloRequest{Name: "CN"})
if err != nil {
fmt.Printf("could not greet: %v", err)
}
fmt.Printf("Greeting: %s !\n", r.Message)
}
针对两种场景的超时:
//timeout, cancelFunc := context.WithTimeout(context.Background(), time.Second*2)
//defer cancelFunc()
m, _ := time.ParseDuration("1s")
result := time.Now().Add(m)
deadline, c2 := context.WithDeadline(context.Background(), result)
defer c2()
分别做了测试,大家可以运行一下代码看看效果。都会看到报错信息:
code = DeadlineExceeded desc = context deadline exceeded
所以超时控制可以通过 Context 来操作,不必你自己再去额外写代码。
跟我一起学Go系列:gRPC 全局数据传输和超时处理的更多相关文章
- 跟我一起学 Go 系列:gRPC 拦截器
Go gRPC 学习系列: 跟我一起学Go系列:gRPC 入门必备 第一篇内容我们已经基本了解到 gRPC 如何使用 .对应的三种流模式.现在已经可以让服务端和客户端互相发送消息.本篇仍然讲解功能性的 ...
- 跟我一起学Go系列:Go gRPC 安全认证机制-SSL/TLS认证
Go gRPC 系列: 跟我一起学Go系列:gRPC 拦截器使用 跟我一起学Go系列:gRPC 入门必备 第一篇入门说过 gRPC 底层是基于 HTTP/2 协议的,HTTP 本身不带任何加密传输功能 ...
- 跟我一起学Go系列:Go gRPC 安全认证方式-Token和自定义认证
Go gRPC 系列: 跟我一起学Go系列:gRPC安全认证机制-SSL/TLS认证 跟我一起学 Go 系列:gRPC 拦截器使用 跟我一起学 Go 系列:gRPC 入门必备 接上一篇继续讲 gRPC ...
- .net基础学java系列(二)IDE
上一篇文章.net基础学java系列(一)视野 废话: "视野"这篇文章,管理员说它比较空洞!也许初学者看不懂表格中的大部分内容!多年的neter估计也有很多不知道的! 有.net ...
- 三叔学FPGA系列之二:Cyclone V中的POR、配置、初始化,以及复位
对于FPGA内部的复位,之前一直比较迷,这两天仔细研究官方数据手册,解开了心中的诸多疑惑,感觉自己又进步了呢..... 原创不易,转载请转原文,注明出处,谢谢. 一.关于POR(Power-On ...
- 跟着鸟哥学Linux系列笔记3-第11章BASH学习
跟着鸟哥学Linux系列笔记0-扫盲之概念 跟着鸟哥学Linux系列笔记0-如何解决问题 跟着鸟哥学Linux系列笔记1 跟着鸟哥学Linux系列笔记2-第10章VIM学习 认识与学习bash 1. ...
- 跟着鸟哥学Linux系列笔记2-第10章VIM学习
跟着鸟哥学Linux系列笔记0-扫盲之概念 跟着鸟哥学Linux系列笔记0-如何解决问题 跟着鸟哥学Linux系列笔记1 常用的文本编辑器:Emacs, pico, nano, joe, vim VI ...
- 跟着鸟哥学Linux系列笔记0-如何解决问题
跟着鸟哥学Linux系列笔记0-扫盲之概念 在发生问题怎么处理: 1. 在自己的主机.网络数据库上查询How-To或FAQ -Linux 自身的文件数据: /usr/share/doc -CLDP中 ...
- 跟着鸟哥学Linux系列笔记1
跟着鸟哥学Linux系列笔记0-扫盲之概念 跟着鸟哥学Linux系列笔记0-如何解决问题 装完linux之后,接下来一步就是进行相关命令的学习了 第五章:首次登录与在线求助man page 1. X ...
随机推荐
- CUDA 7流简化并发
CUDA 7流简化并发 异构计算是指有效使用系统中的所有处理器,包括CPU和GPU.为此,应用程序必须在多个处理器上同时执行功能.CUDA应用程序通过在流(按顺序执行的命令序列)中,执行异步命令来管理 ...
- 在OpenShift平台上验证NVIDIA DGX系统的分布式多节点自动驾驶AI训练
在OpenShift平台上验证NVIDIA DGX系统的分布式多节点自动驾驶AI训练 自动驾驶汽车的深度神经网络(DNN)开发是一项艰巨的工作.本文验证了DGX多节点,多GPU,分布式训练在DXC机器 ...
- 使用NVIDIA A100 TF32获得即时加速
使用NVIDIA A100 TF32获得即时加速 NVIDIA A100带来了我们公司历史上最大的单代性能增长.这是一个新的结构创新,这是一个多功能的支持,这是一个多功能的结构支持.TF32是用于深度 ...
- 如何保证Qt状态机的最佳性能
如何保证Qt状态机的最佳性能 How to ensure the best Qt state machine performance 如果您使用Qt进行应用程序开发,并且使用状态机,那么很可能您正在使 ...
- Java如何使用while和for嵌套循环控制输出数据,使数据奇偶行不同
/* 题目1 使用Eclipse编写控制台应用程, 使用while循环在控制台打印10行10列的如下图形 □ □ □ □ □ □ □ □ □ □ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ □ □ □ □ ...
- 十、使用Varnish加速Web
使用Varnish加速Web 构建Web服务器(web1) [root@web1 ~]# yum -y install httpd [root@web1 ~]# systemctl start ...
- jd一面面经
1.讲一下hashmap原理. 2.concurrentHashMap的原理.concurrentHashMap和hashmap有什么不同? 3.synchnized底层实现?讲讲AQS 4.sync ...
- Spring Boot WebFlu-05——WebFlux 中 Thymeleaf 和 MongoDB 实践
第05课:WebFlux 中 Thymeleaf 和 MongoDB 实践 前言 本节内容主要还是总结上面两篇内容的操作,并实现一个复杂查询的小案例,那么没安装 MongoDB 的可以进行下面的安装流 ...
- HTTP请求方法及响应状态码详解
HTTP请求方法和响应状态详解 HTTP请求方法 HTTP1.0/1.1支持的所有请求方法如下所示: GET 用来请求访问已被URI识别的资源.指定的资源经服务器解析后返回响应内容. POST POS ...
- git命令使用(必备系列)
git是一个分布式版本控制系统,得益于高效.协作和快速的项目代码管理特性几乎每一个软件开发团队都在深度使用.本篇是对git命令的介绍,涵盖了不低于95%的日常操作命令,对你有用话可以收藏一下哦. 一. ...