第三期:gRPC客户端与服务端连接失败后,是否会有重试机制?
grpc 版本1.50
client.go 代码:
func main() { flag.Parse() // Set up a connection to the server. conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() c := pb.NewGreeterClient(conn)
// Contact the server and print out its response. ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name}) if err != nil { log.Fatalf("could not greet: %v", err) } log.Printf("Greeting: %s", r.GetMessage())}
Dial 源码:
func Dial(target string, opts ...DialOption) (*ClientConn, error) { return DialContext(context.Background(), target, opts...)}
DialContext 源码:
省略次部分代码
// 首先会创建 ClientConn,初始化相关字段func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) { cc := &ClientConn{ target: target, csMgr: &connectivityStateManager{}, conns: make(map[*addrConn]struct{}), dopts: defaultDialOptions(), blockingpicker: newPickerWrapper(), czData: new(channelzData), firstResolveEvent: grpcsync.NewEvent(), }
// 将用户设置的连接参数更新到客户端连接器 ClientConn for _, opt := range opts { opt.apply(&cc.dopts) }
return cc, nil}
connect() 方法:
func (ac *addrConn) connect() error { ac.mu.Lock() // if 校验状态 if ac.state == connectivity.Shutdown { ac.mu.Unlock() return errConnClosing } if ac.state != connectivity.Idle { ac.mu.Unlock() return nil } ac.updateConnectivityState(connectivity.Connecting, nil) ac.mu.Unlock() // 主要看这个方法,重试连接 ac.resetTransport() return nil}
进入 resetTransport() 源码
func (ac *addrConn) resetTransport() { ac.mu.Lock() // 判断状态若为 shutdown,则不再连接直接推出 if ac.state == connectivity.Shutdown { ac.mu.Unlock() return }
addrs := ac.addrs // 连接失败,需要进行重试的 // Backoff 是需要等待的时间,ac.backoffIdx表示第几次重试 backoffFor := ac.dopts.bs.Backoff(ac.backoffIdx) // 计算出本次向gRPC服务尝试建立TCP连接的最长时间 // 若超过这个时间还是连接不上,则主动断开,等待尝试下次连接 // This will be the duration that dial gets to finish. dialDuration := minConnectTimeout if ac.dopts.minConnectTimeout != nil { dialDuration = ac.dopts.minConnectTimeout() }
if dialDuration < backoffFor { // Give dial more time as we keep failing to connect. dialDuration = backoffFor } connectDeadline := time.Now().Add(dialDuration)
// 更新结构addrConn状态为connecting ac.updateConnectivityState(connectivity.Connecting, nil) ac.mu.Unlock()
// 向服务器连接失败后需要做的逻辑 if err := ac.tryAllAddrs(addrs, connectDeadline); err != nil { ac.cc.resolveNow(resolver.ResolveNowOptions{}) // After exhausting all addresses, the addrConn enters // TRANSIENT_FAILURE. ac.mu.Lock() if ac.state == connectivity.Shutdown { ac.mu.Unlock() return } ac.updateConnectivityState(connectivity.TransientFailure, err)
// Backoff. b := ac.resetBackoff ac.mu.Unlock()
// 定时的超时间 timer := time.NewTimer(backoffFor) // 1.timer.C如果连接超时,重试的次数+1,继续下一次重试连接,但需要等待一段时间 // 2. b,直接关闭,将继续进行重新连接 // 2. ac.ctx.Done,走这里的话,上下文结束,这里不会再次重试了 select { case <-timer.C: ac.mu.Lock() ac.backoffIdx++ ac.mu.Unlock() case <-b: timer.Stop() case <-ac.ctx.Done(): timer.Stop() return }
ac.mu.Lock() // 状态 != shutdown就更新为空闲状态 if ac.state != connectivity.Shutdown { ac.updateConnectivityState(connectivity.Idle, err) } ac.mu.Unlock() return } // 连接成功,重新设置backoff为原始值0 ac.mu.Lock() ac.backoffIdx = 0 ac.mu.Unlock()}
如何计算重试连接等待时间?
进入Backoff方法
func (bc Exponential) Backoff(retries int) time.Duration { if retries == 0 { return bc.Config.BaseDelay } backoff, max := float64(bc.Config.BaseDelay), float64(bc.Config.MaxDelay) for backoff < max && retries > 0 { // 幂次方 backoff *= bc.Config.Multiplier retries-- } // 不能超过最大延时时间 if backoff > max { backoff = max } // Randomize backoff delays so that if a cluster of requests start at // the same time, they won't operate in lockstep. backoff *= 1 + bc.Config.Jitter*(grpcrand.Float64()*2-1) if backoff < 0 { return 0 } return time.Duration(backoff)}
总结:
连接失败后,客户端会进行重试连接。
重试次数越多,等待下一次连接时间也会变长,但不能超过MaxDelay值。
更多Go云原生学习资料,收录于Github:https://github.com/metashops/GoFamily
本文由 mdnice 多平台发布
第三期:gRPC客户端与服务端连接失败后,是否会有重试机制?相关推荐
- 浅谈MySQL中客户端与服务端连接方式
一.前言 前面五篇文章给大家介绍了如何安装数据库到一条SQL在服务端需要经历那些步骤才能够解析完成,相信大家对数据库也有了初步的了解,但俗话说的好"纸上谈兵不如躬行实践",前面学习 ...
- GRPC 客户端释放channel资源失败或者卡死的解决方案
GRPC 客户端在某些情况下会出现资源无法释放? 当我们创建一个全局的对象时,或者静态对象是,或者智能指针,并期望系统在进程退出的时候自动回收申请的内存.但不幸的是,系统回收资源无法按我们期望的顺序, ...
- 【微服务】Nacos通知客户端服务变更以及重试机制
- linux epoll机制对TCP 客户端和服务端的监听C代码通用框架实现
1 TCP简介 tcp是一种基于流的应用层协议,其"可靠的数据传输"实现的原理就是,"拥塞控制"的滑动窗口机制,该机制包含的算法主要有"慢启动&quo ...
- 封装利用libwebsockets写出的客户端、服务端程序为客户端服务端类
封装利用libwebsockets写出的客户端.服务端程序为客户端服务端类 文章目录 封装利用libwebsockets写出的客户端.服务端程序为客户端服务端类 1.封装 2.封装后写wss客户端.服 ...
- 从零开始构建gRPC的Go服务
介绍 Protocol Buffers and gRPC是用于定义通过网络有效通信的微服务的流行技术.许多公司在Go中构建gRPC微服务,发布了他们开发的框架,本文将从gRPC入门开始,一步一步构建一 ...
- 布道微服务_17服务调用失败的解决方案
文章目录 Pre 服务调用失败的处理手段 超时 重试 双发 熔断 小结 Pre 我们知道,微服务相比于单体应用最大的不同之处在于,服务的调用从同一台机器内部的本地调用变成了不同机器之间的远程方法调用, ...
- 【Spring Cloud】OpenFeign和Spring Cloud Loadbalancer调用失败后的重试机制比较
1 概述 搭建一个微服务系统,有两个服务,Client和Server,Server有三个实例A.B.C,我让Client调用Server,Loadbalancer负载分担默认采用轮询机制,当Serve ...
- RPC 笔记(03)— gRPC 概念、安装、编译、客户端和服务端示例
1. gRPC 概念 gRPC 是 Google 开源的一款高性能的 RPC 框架.GitHub 上介绍如下: gRPC is a modern, open source, high-performa ...
最新文章
- ES6深入学习记录(一)class方法相关
- 基于JavaEE实现网上拍卖系统
- gridreport如何设置打印3次_如何设置光固化3D打印机切片参数
- 5G时代即将来临,三大运营商各自为营谁能抢占先机?
- 使用iai_kinect2标定kinectV2相机
- SpringMVC学习一
- 卷积,DFT,FFT,图像FFT,FIR 和 IIR 的物理意义。
- [转]SQL操作全集
- 机器视觉工业光源知识总结
- PHP数字金额转换成中文大写金额
- Unity3D中常用的物理学公式
- linux apache 查看mpm 配置方式,apache httpd mpm配置
- UFS系列三:UFS数据包UPIU
- echarts柱状图加上数量
- 作家天地杂志作家天地杂志社作家天地编辑部2022年第23期目录
- 【kafka】Error while fetching metadata xxx: {TEST=LEADER_NOT_AVAILABLE}
- windows保护无法启动修复服务器,如何解决win10“Windows资源保护无法启动修复服务”错误 | MOS86...
- NOIP模拟赛 队爷的 Au Plan
- FPGA之FIFO详解,初识FIFO
- PAT_乙级1012