gRPC 的 ClientConn 到底管着几条连接?什么时候建立?连接断了怎么重连、隔多久?服务端优雅关闭时客户端看到什么?空闲连接会被关吗?这篇逐项验证。
NewClient 之后 200 ms:状态 IDLE;代理看到的 TCP 连接 0 条 第一次调用之后:状态 READY;TCP 连接 1 条 再调用 20 次之后:状态 READY;TCP 连接仍是 1 条 另一个 ClientConn 只调用 Connect(),不发请求:状态 READY;TCP 连接共 2 条
grpc.NewClient 不会连接,创建后状态是 IDLE。第一次调用或 Connect() 才建 TCP 连接;之后所有调用复用这一条。
pick_first 30 次调用之后,三个后端各自收到的 TCP 连接数 [1 0 0] round_robin 30 次调用之后,三个后端各自收到的 TCP 连接数 [1 1 1]
每个后端地址一个子连接。pick_first 只连一个地址;round_robin 对所有地址都建连接并轮流使用(第 14 篇)。
默认退避:11 秒内尝试 5 次,时刻: 0.00s (+1.00s) 1.00s (+1.55s) 2.56s (+2.24s) 4.80s (+4.79s) 9.59s
最后状态 TRANSIENT_FAILURE
自定义退避(基础 100 ms、倍数 2、抖动 0、最大 1 s):11 秒内尝试 14 次,时刻: 0.00s (+0.10s) 0.10s (+0.20s) 0.30s (+0.40s) 0.70s (+0.80s) 1.50s (+1.00s) 2.51s (+1.00s) 3.51s (+1.00s) 4.51s (+1.00s) 5.51s (+1.00s) 6.51s (+1.00s) 7.51s (+1.00s) 8.52s (+1.00s) 9.52s (+1.00s) 10.52s
最后状态 TRANSIENT_FAILURE连不上时按指数退避重试:默认基础 1 秒、倍数 1.6、抖动 20%、最长 120 秒(读源码 backoff/backoff.go)。可以用 WithConnectParams 调整。
服务端 Stop 之后正在进行的调用:code=Unavailable message="error reading from server: EOF"(0.2 ms 后返回) 连接状态 IDLE 退避期间的新调用(fail-fast):code=Unavailable,0.3 ms 返回 服务端重启后用 WaitForReady(true) 调用:err=<nil>,等了 301.2 ms(退避结束后重连成功才发出) 连接状态 READY
连接断开后客户端进入 IDLE,等下一次调用才重连;fail-fast 调用在退避期间立即失败,WaitForReady 会等到重连成功。
GracefulStop 开始后发起的新调用:code=Unavailable message="connection error: desc = \"error reading server preface: read tcp 127.0.0.1:55557->127.0.0.1:9806: read: connection reset by peer\"" 在途的慢调用:err=<nil>,399.4 ms 后完成 线上 S→C GOAWAY:最后流 2147483647,错误码 NO_ERROR,附加数据 "graceful_stop" 线上 S→C GOAWAY:最后流 3,错误码 NO_ERROR,附加数据 "graceful_stop" 连接状态 TRANSIENT_FAILURE
服务端先发最后流 ID 为 2147483647 的 GOAWAY,再发带真实最后流 ID 的 GOAWAY;在途调用完成,新调用失败。
第一次调用之后:状态 READY,TCP 连接 1 条 空闲 700 ms 后:状态 IDLE 再调用一次:2.1 ms,状态 READY,TCP 连接累计 2 条(重新建立了连接)
WithIdleTimeout 让空闲的连接关闭并回到 IDLE,下一次调用重新建立,可避开被中间设备悄悄断掉的连接(第 40 篇)。