golang在GRPC中设置client的超时时间


Posted in Golang onApril 27, 2021

超时

建立连接

主要就2函数Dail和DialContext。

// Dial creates a client connection to the given target.
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
    return DialContext(context.Background(), target, opts...)
}
func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error){...}

DialContext 太长了不帖了.看Dial实际上也是调用DialContext来实现的.如果你想在建立连接的时候使用超时控制.就使用

DialContext传入一个Timeout的context,就像下面的例子

ctx1, cel := context.WithTimeout(context.Background(), time.Second*3)
defer cel()
conn, err := grpc.DialContext(ctx1, address, grpc.WithBlock(), grpc.WithInsecure())

另外调用Dial建立连接默认只是返回一个ClientConn的指针,相当于new了一个ClientConn 把指针返回给你。并不是一定要建立真实的h2连接.至于真实的连接建立实际上是一个异步的过程。

当然了如果你想等真实的链接完全建立再返回ClientConn可以通过WithBlock传入Options来实现,当然了这样的话链接如果建立不成功就会一直阻塞直到Contex超时。

真正的建立链接的代码后面介绍重试的时候会再详细介绍。

调用超时

这个比较简单

ctx, cancel := context.WithTimeout(context.TODO(), time.Second*3)
defer cancel()
 r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})

如上代码传入一个timeout context就可以。

Server

type SearchService struct{}
 
func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) {
    for i := 0; i < 5; i++  {
        if ctx.Err() == context.Canceled {
            return nil, status.Errorf(codes.Canceled, "SearchService.Search canceled")
        }
 
        time.Sleep(1 * time.Second)
    }
 
    return &pb.SearchResponse{Response: r.GetRequest() + " Server"}, nil
}
 
func main() {
    ...
}

而在 Server 端,由于 Client 已经设置了截止时间。Server 势必要去检测它

否则如果 Client 已经结束掉了,Server 还傻傻的在那执行,这对资源是一种极大的浪费

因此在这里需要用 ctx.Err() == context.Canceled 进行判断,为了模拟场景我们加了循环和睡眠 ?

验证

重新启动 server.go 和 client.go,得到结果:

$ go run client.go
2018/10/06 17:45:55 client.Search err: deadline
exit status 1

总结

本章节比较简单,你需要知道以下知识点:

怎么设置 Deadlines

为什么要设置 Deadlines

你要清楚地明白到,gRPC Deadlines 是很重要的,否则这小小的功能点就会要了你生产的命。

补充:golang使用grpc超时控制和对冲策略

超时控制

grcp超时控制设置在客户端调用服务时,如果设定了超时时间,客户端会立即返回超时。超时控制一般有三个因素:链路超时:上有调用端通过协议字段把自己允许的超时时间传给当前服务,表示在该时间内返回数据,超时返回已无意义。流程如下图A调用B的总超时情况。

消息超时:服务端收到请求消息到返回响应数据的最长消息处理时间。下图的B内部的当前请求整体超时时间。调用超时:当前服务调用下游服务设置的每一个rpc请求的超时时间。如下图B调用C的单个超时时间。通常一次请求会连续调用多次rpc,这个调用超时控制的是每个rpc的独立超时时间。

golang在GRPC中设置client的超时时间

发起rpc调用请求时,需要计算此次rpc调用的超时时间。真正生效的超时时间是通过以上三个因素实时计算的最小值,计算过程

如下:

1、首先计算链路超时和消息超时的最小值,如链路超时2s,消息超时1s,则当前消息的最长处理时间为1s。

2、发起rpc调用时,再次计算当前消息最长处理时间和单个超时时间的最小值,比如:上图的B->C设置的单个超时时间为5s,则实际上B调用C的真实超时仍然是1s,其实只要超时时间大于当前最长处理时间都是无效的,都会取最小值。再比如B->C单个超时时间为500ms,这种情况B调用C的真实超时即为500ms,此时500ms这个值也会通过协议字段传给C,在服务端C的视角来看就是他的链路超时时间。链路超时时间会在整个rpc调用链上一直传递下去,并逐渐减少,直至为0,这样避免出现死循环调用的问题。

3、因为每一次rpc调用都会实际消耗一部分时间,所以当前消息最长处理时间需要实时计算剩余时间,比如上面B调用C真实耗时200ms,此时最长处理时间就只剩下800ms了。此时发起第二次rpc调用时,则需要计算此时剩余的消息超时时间和单个调用时间的最小值。如上图的B->D设置的单个超时时间为1s,则实际生效的超时时间仍然为800ms。链路超时设置:golang的context.Context根据协议里面的timeout字段和框架配置的timeout字段。设置好当前请求的最长处理时间,然后交给用户使用,并在处理函数结束时会立马cancel掉当前context。所以在创建新的goroutine时,需要重新设定新的context。

对冲策略

对冲策略不是被动的等待上一次请求超时或者失败,在对冲延时时间内(或小于超时时间)如果没有收到回复的包就会再触发一个请求。

与重试策略不同的是同一时间内in-fliaght可能有多个,当接收到第一回复时,其他的回复会被忽略。

一、重试策略:

对失败的请求,进行重新请求。

golang在GRPC中设置client的超时时间

由图中可以看出,client一共进行了三次请求,前两次均失败,并且在重新请求时都会随机避段时间,防止请求毛刺,第三次请求成功,返回给应用层。对于每次尝试,我们都会尽可能地将请求发往不同的节点。

通常重试策略有三种配置:

1、失败重新请求的最大次数,达到最大次数仍然失败,不再进行重试;

2、退避时间:退避时间取的是 random(0, delay);

3、可重试错误码:设置可错误码,对于不可重试的,立即停止重试并将错误返回应用层。

二、对冲策略

golang在GRPC中设置client的超时时间

上图中client一共进行了4次,橙、蓝、绿、紫

橙色是第一次尝试。在由 client 发起后,server2 很快便收到了。但是 server2 的因为网络等问题,直到绿色请求成功,并返回给应用层后,它的正确回包才姗姗来迟。尽管它成功了,但我们必须丢弃它,因为我们已经将另一个成功的回包返回给应用层了。

蓝色是第二次尝试。因为橙色请求在对冲时延(hedging delay)后还没有回包,因此我们发起了一次新的尝试。这次尝试选择了 server1(我们会尽可能地为每次尝试选择不同的节点)。蓝色尝试的回包比较快,在对冲时延之前便返回了。但是却失败了。我们立刻发起了新一次尝试。

绿色是第三次尝试。尽管它的回包可能有点慢(超过了对冲时延,因此又触发了一次新的尝试),但是它成功了!一旦我们收到第一个成功的回包,便立刻将它返回给了应用层。

紫色是第四次尝试。刚发起后,我们便收到了绿色成功的回包。对紫色来说,它可能处于很多状态:请求还在 client gRPC 内,这时,我们有机会取消它;请求已经进入了 client 的内核或者已经由网卡发出,无论如何,我们已经没有机会取消它了。紫色请求上的 ✘ 表示我们会尽可能地取消紫色请求。注意,即使紫色请求最终成功地到达了 server2,它的回包也会像橙色一样被丢弃。

由以上可知对冲策略更像是添加了等待时间的重试,但是他没有退避机制,一旦收到错误的包,立刻发起重试。这种对于需要解决长尾问题时推荐使用,一般情况建议使用重试策略。

对冲策略一般有三种配置

1、对冲延时:在对对冲时延内没有收到回包时便会立刻发起新的尝试;

2、最大请求次数:一旦耗尽,便等待并返回最后一个回包,无论它是否成功或失败;

3、非致命错误:返回致命错误会立刻中止对冲,等待并返回最后一个回包,无论它是否成功或失败。返回非致命错误会立刻触发一次新的尝试(对冲时延计时器会被重置)。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Golang 相关文章推荐
go语言map与string的相互转换的实现
Apr 07 Golang
golang通过递归遍历生成树状结构的操作
Apr 28 Golang
golang json数组拼接的实例
Apr 28 Golang
Golang 使用Map实现去重与set的功能操作
Apr 29 Golang
Go语言设计模式之结构型模式
Jun 22 Golang
Go 语言下基于Redis分布式锁的实现方式
Jun 28 Golang
Golang 并发下的问题定位及解决方案
Mar 16 Golang
golang为什么要统一错误处理
Apr 03 Golang
如何解决goland,idea全局搜索快捷键失效问题
Apr 03 Golang
Golang 1.18 多模块Multi-Module工作区模式的新特性
Apr 11 Golang
golang使用map实现去除重复数组
Apr 14 Golang
Golang jwt身份认证
Apr 20 Golang
golang http使用踩过的坑与填坑指南
Apr 27 #Golang
Golang 实现超大文件读取的两种方法
Apr 27 #Golang
golang中的空slice案例
Apr 27 #Golang
Go语言切片前或中间插入项与内置copy()函数详解
golang中切片copy复制和等号复制的区别介绍
Apr 27 #Golang
go语言中切片与内存复制 memcpy 的实现操作
Apr 27 #Golang
Go语言中的UTF-8实现
Apr 26 #Golang
You might like
php入门小知识
2008/03/24 PHP
PHP中simplexml_load_string函数使用说明
2011/01/01 PHP
php利用header函数下载各种文件
2016/08/24 PHP
PHP双向链表定义与用法示例
2018/01/31 PHP
use jscript List Installed Software
2007/06/11 Javascript
用javascript实现计算两个日期的间隔天数
2007/08/14 Javascript
检测jQuery.js是否已加载的判断代码
2011/05/20 Javascript
Javascript图片上传前的本地预览实例
2014/06/16 Javascript
JavaScript如何自定义trim方法
2015/07/28 Javascript
AngularJS中实现显示或隐藏动画效果的方式总结
2015/12/31 Javascript
深入理解MVC中的时间js格式化
2016/05/19 Javascript
JS代码实现table数据分页效果
2016/05/26 Javascript
JS读写CSS样式的方法汇总
2016/08/16 Javascript
Node.js发送HTTP客户端请求并显示响应结果的方法示例
2017/04/12 Javascript
浅谈Koa2框架利用CORS完成跨域ajax请求
2018/03/06 Javascript
详解VUE中常用的几种import(模块、文件)引入方式
2018/07/03 Javascript
vue+iview+less 实现换肤功能
2018/08/17 Javascript
如何基于JS截获动态代码
2019/12/25 Javascript
微信小程序地图实现展示线路
2020/07/29 Javascript
Node.js 深度调试方法解析
2020/07/28 Javascript
js实现简单的轮播图效果
2020/12/13 Javascript
[39:18]完美世界DOTA2联赛PWL S3 Forest vs LBZS 第二场 12.17
2020/12/19 DOTA
python海龟绘图实例教程
2014/07/24 Python
Python操作mysql数据库实现增删查改功能的方法
2018/01/15 Python
Python实现的凯撒密码算法示例
2018/04/12 Python
详解python读取和输出到txt
2019/03/29 Python
python3 json数据格式的转换(dumps/loads的使用、dict to str/str to dict、json字符串/字典的相互转换)
2019/04/01 Python
CSS3 实现童年的纸飞机
2019/05/05 HTML / CSS
美国汽车轮胎和轮毂销售网站:Tire Rack
2018/01/11 全球购物
目标责任书范本
2014/04/16 职场文书
小学课外阅读总结
2014/07/09 职场文书
2015年依法行政工作总结
2015/04/29 职场文书
大学运动会加油稿
2015/07/22 职场文书
小学音乐课教学反思
2016/02/18 职场文书
Pygame Draw绘图函数的具体使用
2021/11/17 Python
Mysql数据库表中为什么有索引却没有提高查询速度
2022/02/24 MySQL