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中semaphore(信号量)源码
Apr 03 Golang
Go语言操作数据库及其常规操作的示例代码
Apr 21 Golang
浅谈golang 中time.After释放的问题
May 05 Golang
Golang 获取文件md5校验的方法以及效率对比
May 08 Golang
go语言中http超时引发的事故解决
Jun 02 Golang
基于Go语言构建RESTful API服务
Jul 25 Golang
Golang 并发下的问题定位及解决方案
Mar 16 Golang
简单聊聊Golang中defer预计算参数
Mar 25 Golang
golang操作redis的客户端包有多个比如redigo、go-redis
Apr 14 Golang
golang生成并解析JSON
Apr 14 Golang
Golang MatrixOne使用介绍和汇编语法
Apr 19 Golang
Golang获取List列表元素的四种方式
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中理解print EOT分界符和echo EOT的用法区别小结
2010/02/21 PHP
php写的简易聊天室代码
2011/06/04 PHP
通过curl模拟post和get方式提交的表单类
2014/04/23 PHP
php中file_get_contents与curl性能比较分析
2014/11/08 PHP
Laravel+jQuery实现AJAX分页效果
2016/09/14 PHP
解决PHP上传非标准格式的图片pjpeg失败的方法
2017/03/12 PHP
PHP操作MongoDB实现增删改查功能【附php7操作MongoDB方法】
2018/04/24 PHP
thinkPHP5框架auth权限控制类与用法示例
2018/06/12 PHP
javascipt:filter过滤介绍及使用
2014/09/10 Javascript
js实现完全自定义可带多级目录的网页鼠标右键菜单方法
2015/02/28 Javascript
详解JavaScript中双等号引起的隐性类型转换
2016/05/30 Javascript
浅谈js图片前端预览之filereader和window.URL.createObjectURL
2016/06/30 Javascript
vue.js中mint-ui框架的使用方法
2017/05/12 Javascript
微信小程序 地图map实例详解
2017/06/07 Javascript
基于node.js制作简单爬虫教程
2017/06/29 Javascript
什么是Vue.js框架 为什么选择它?
2017/10/17 Javascript
JavaScript使用indexOf()实现数组去重的方法分析
2018/09/04 Javascript
vue自定义指令实现方法详解
2019/02/11 Javascript
vue基础之模板和过滤器用法实例分析
2019/03/12 Javascript
详解vue组件之间的通信
2020/08/30 Javascript
[01:03:37]Secret vs VGJ.S Supermajor小组赛C组 BO3 第二场 6.3
2018/06/04 DOTA
跟老齐学Python之总结参数的传递
2014/10/10 Python
pycharm修改界面主题颜色的方法
2019/01/17 Python
Python动态参数/命名空间/函数嵌套/global和nonlocal
2019/05/29 Python
python开发之anaconda以及win7下安装gensim的方法
2019/07/05 Python
详解mac python+selenium+Chrome 简单案例
2019/11/08 Python
python 中值滤波,椒盐去噪,图片增强实例
2019/12/18 Python
浅析Python3 pip换源问题
2020/01/06 Python
用python 绘制茎叶图和复合饼图
2021/02/26 Python
小学六年级学生评语
2014/04/22 职场文书
党员干部一句话承诺
2014/05/30 职场文书
环境保护标语
2014/06/20 职场文书
师范生见习报告
2014/10/31 职场文书
手机销售员岗位职责
2015/04/11 职场文书
王亚平太空授课观后感
2015/06/12 职场文书
优秀共产党员主要事迹材料
2015/11/05 职场文书