网络重置后客户端通道不可用

Ant*_*yev 5 c++ grpc

总结:如果客户端通道处于某种READY状态并且网络断开,则通道将变得不可用,并且一旦重新建立网络连接,客户端将不会尝试重新连接到服务器。通道不会从READY状态转换TRANSIENT_FAILUREDEADLINE_EXCEEDED错误(由我的客户端应用程序设置的截止日期)。

您使用的是什么版本的 gRPC 和什么语言?

1.17.2 版本 1.11.x C++ 相同问题体验

什么操作系统(Linux、Windows 等)和版本?

在 Ubuntu 16.04 上运行的客户端。运行 Windows Enterprise 的服务器。

你做了什么?

服务器和客户端都在连接的网络上启动。我可以成功拨打电话并接收来自服务器的响应。当网络关闭时,服务器会收到"Disconnected client - Endpoint read failed"错误消息。此调试消息中的其他一些相关字段 - "grpc_status":14 (UNAVAILABLE), "occured_during_write":0, "description":"An established connection was aborted by the software in your host machine"

在网络断开时,客户端根本不打印任何日志(使用GRPC_TRACE=connectivity_state,call_error,op_failure,server_channel,client_channel,channel GRPC_VERBOSITY=DEBUG)。

再次打开网络后,服务器和客户端都没有日志记录。尝试使用客户端进行调用(发送启动请求)会导致重复DEADLINE_EXCEEDED错误。此时关闭网络连接不会导致服务器端"Disconnected client"错误。

客户端上下文设置为使用截止日期(测试时间为 2 秒和 10 秒)。在这种情况下使用同步调用。

代码片段:

/rpc_service.proto

syntax = "proto3";

import "google/rpc/status.proto";

message RpcRequest {
}

message RpcResponse {
}

service RpcService{
rpc Call(RpcRequest) returns (RpcResponse);
}
Run Code Online (Sandbox Code Playgroud)

/client.cc

初始化:

std::unique_ptrRpcService::Stub stub_ = RpcService::NewStub(::grpc::CreateChannel(
server_endpoint, ::grpc::InsecureChannelCredentials()));
Run Code Online (Sandbox Code Playgroud)

发送 rpc 请求:

::grpc::ClientContext context;
context.set_deadline(
gpr_time_from_micros(call_timeout_.InMicroseconds(), GPR_TIMESPAN));
RpcRequest request;
RpcResponse response;
::grpc::Status grpc_status = stub_->Call(&context, request, &response);
Run Code Online (Sandbox Code Playgroud)

/server.cc

grpc::ServerBuilder builder;
builder.AddListeningPort(endpoint, ::grpc::InsecureServerCredentials());
builder.RegisterService(&rpc_service);
std::unique_ptrgrpc::Server grpc_server_ = builder.BuildAndStart();
Run Code Online (Sandbox Code Playgroud)

你期待看到什么?

客户端应在网络重置后成功调用。

你看到了什么?

客户端无法从服务器接收响应。

关于您的项目/环境,我们还应该了解什么?

当重新建立网络连接并且客户端无法收到服务器的响应时,tcpdump 会捕获客户端发送的一些数据包。在网络打开的情况下启动客户端和服务器,然后在尝试调用之前拔掉网络不会导致任何错误消息。这与在网络断开连接的情况下启动客户端和服务器时的结果相同。一旦尝试调用,客户端将从IDLEto转换CONNECTING,然后开始在CONNECTINGandTRANSIENT_FAILURE状态之间来回反弹(尝试使用指数退避重新连接),直到重新建立连接。

如果客户端在连接网络的情况下启动,但不发送请求并且网络断开连接,则服务器不会收到断开连接的客户端错误。在进行呼叫之前,客户端将停留在"IDLE".

如果客户端已初始化并在断开连接的网络上进行呼叫,则客户端将进入一种CONNECTING状态(指数退避最多 2 分钟,客户端将处于该TRANSIENT_FAILURE状态)。一旦网络连接好,下一次通道进入CONNECTING状态时会重新建立连接,客户端进入READY状态。此后,每次调用都会成功,直到网络重置。在客户端处于某种READY状态后断开网络连接不会使客户端脱离READY状态。

总结:在调用之前,"IDLE"无论网络状态如何,客户端都会保持一种状态。一旦发出呼叫,客户端将尝试通过进入该CONNECTING状态来建立连接。如果没有找到连接,它将在CONNECTINGTRANSIENT_FAILURE状态之间转换反弹。一旦找到连接,客户端将进入一种READY状态。从这里开始,如果连接丢失,客户端将不会CONNECTING再次尝试进入状态。

与我遇到的类似问题(已关闭):

https://github.com/grpc/grpc/issues/16974

已知修复:

在每次通话时创建一个新频道。

失败的修复尝试:

Set GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA = 0
Run Code Online (Sandbox Code Playgroud)

问题:

网络重置后,客户端是否可以使用已创建的通道?

网络重置时是否必须重新启动通道?

注意:我在grpc github上开了一个票,5天了还没有收到回复,所以我也在这里发帖。链接到 grpc github 问题:https : //github.com/grpc/grpc/issues/18554