Go 执行与gRPC客户端重新连接的正确方法
我有一个Go gRPC客户端连接到运行在k8s群集中另一个pod中的gRPC服务器 它工作正常,接收和处理请求 我现在想知道,如果gRPC服务器pod被回收,如何最好地实现恢复能力 据我所知,clientconn.go代码应该自动处理重新连接,但我就是无法让它工作,我担心我的实现在一开始是不正确的 从main调用代码:Go 执行与gRPC客户端重新连接的正确方法,go,kubernetes,network-programming,grpc,Go,Kubernetes,Network Programming,Grpc,我有一个Go gRPC客户端连接到运行在k8s群集中另一个pod中的gRPC服务器 它工作正常,接收和处理请求 我现在想知道,如果gRPC服务器pod被回收,如何最好地实现恢复能力 据我所知,clientconn.go代码应该自动处理重新连接,但我就是无法让它工作,我担心我的实现在一开始是不正确的 从main调用代码: go func() { if err := gRPCClient.ProcessRequests(); err != nil {
go func() {
if err := gRPCClient.ProcessRequests(); err != nil {
log.Error("Error while processing Requests")
//do something here??
}
}()
gRPCClient包装模块中的我的代码:
func (grpcclient *gRPCClient) ProcessRequests() error {
defer grpcclient.Close()
for {
request, err := reqclient.stream.Recv()
log.Info("Request received")
if err == io.EOF {
break
}
if err != nil {
//when pod is recycled, this is what's hit with err:
//rpc error: code = Unavailable desc = transport is closing"
//what is the correct pattern for recovery here so that we can await connection
//and continue processing requests once more?
//should I return err here and somehow restart the ProcessRequests() go routine in the
//main funcition?
break
} else {
//the happy path
//code block to process any requests that are received
}
}
return nil
}
func (reqclient *RequestClient) Close() {
//this is called soon after the conneciton drops
reqclient.conn.Close()
}
编辑:
Emin Laletovic在下面优雅地回答了我的问题,并且在大部分过程中都得到了回答。
我必须对waitUntilReady函数进行一些更改:
func (grpcclient *gRPCClient) waitUntilReady() bool {
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Second) //define how long you want to wait for connection to be restored before giving up
defer cancel()
currentState := grpcclient.conn.GetState()
stillConnecting := true
for currentState != connectivity.Ready && stillConnecting {
//will return true when state has changed from thisState, false if timeout
stillConnecting = grpcclient.conn.WaitForStateChange(ctx, currentState)
currentState = grpcclient.conn.GetState()
log.WithFields(log.Fields{"state: ": currentState, "timeout": timeoutDuration}).Info("Attempting reconnection. State has changed to:")
}
if stillConnecting == false {
log.Error("Connection attempt has timed out.")
return false
}
return true
}
RPC连接由
clientconn.go
自动处理,但这并不意味着流也会自动处理
流一旦中断,无论是由于RPC连接中断还是其他原因,都无法自动重新连接,并且一旦RPC连接恢复,您需要从服务器获取新流
等待RPC连接处于READY
状态并建立新流的伪代码可能如下所示:
func (grpcclient *gRPCClient) ProcessRequests() error {
defer grpcclient.Close()
go grpcclient.process()
for {
select {
case <- grpcclient.reconnect:
if !grpcclient.waitUntilReady() {
return errors.New("failed to establish a connection within the defined timeout")
}
go grpcclient.process()
case <- grpcclient.done:
return nil
}
}
}
func (grpcclient *gRPCClient) process() {
reqclient := GetStream() //always get a new stream
for {
request, err := reqclient.stream.Recv()
log.Info("Request received")
if err == io.EOF {
grpcclient.done <- true
return
}
if err != nil {
grpcclient.reconnect <- true
return
} else {
//the happy path
//code block to process any requests that are received
}
}
}
func (grpcclient *gRPCClient) waitUntilReady() bool {
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) //define how long you want to wait for connection to be restored before giving up
defer cancel()
return grpcclient.conn.WaitForStateChange(ctx, conectivity.Ready)
}
func(grpcclient*grpcclient)ProcessRequests()错误{
延迟grpcclient.Close()
转到grpcclient.process()
为了{
挑选{
感谢您的案例,这让我在大部分方面都做到了。从我的测试来看,waitUntilReady()中存在一个问题,首先WaitForStateChange从提供的状态返回,因此由于状态为TransientFailure,它会立即返回。为了使这项工作正常,我引入了一个for循环,以继续尝试,直到状态变为“就绪”请参阅我的原始帖子了解我的版本。