gRPC C++客户端 超时Deadline Exceeded

0.场景还原

最近许多用户说客户端登录失败,打开日志发现gRPC存根stub在调用接口错误,返回Deadline Exceeded,stub在调用接口时可以指定grpc::ClientContext,来规定超时时间,代码如下:

// 设置超时时间
grpc::ClientContext context;
gpr_timespec ts;
ts.tv_sec = 10;
ts.tv_nsec = 0;
ts.clock_type = GPR_TIMESPAN;
context.set_deadline(ts);
    
m_stub->rpcFunc(&context, request, reply);

返回Deadline Exceeded说明gRPC在调用函数时,10s没有返回结果。


1.请求到底发出去了没有?

下载tcping.exe 放在c:\WINDOWS\system32目录下 tcping -t IP Port  观察本地是否能连的上服务器的端口,Port is open说明能正常连接

PS C:\WINDOWS\system32> tcping -t 14.215.177.39 80

** Pinging continuously.  Press control-c to stop **

Probing 14.215.177.39:80/tcp - Port is open - time=40.968ms
Probing 14.215.177.39:80/tcp - Port is open - time=44.627ms
Probing 14.215.177.39:80/tcp - Port is open - time=41.297ms
Probing 14.215.177.39:80/tcp - Port is open - time=46.088ms
Probing 14.215.177.39:80/tcp - Port is open - time=51.610ms
Control-C

Ping statistics for 14.215.177.39:80
     5 probes sent.
     5 successful, 0 failed.  (0.00% fail)
Approximate trip times in milli-seconds:
     Minimum = 40.968ms, Maximum = 51.610ms, Average = 44.918ms

既然端口是通的,那我们启动下客户端,最后会创建stub,gRPC会和服务端建立起TCP连接,使用netstat -ano | findstr ESTABLISHED   在用户环境使用命令,观察本地是否和服务器建立上TCP连接,这个我发现当前环境并没有和服务端建立起TCP连接,那么就找到问题的关键了,结论是gRPC底层建立TCP连接失败了,所以请求并没有发出去

2.为什么gRPC建立TCP连接失败?

客户端初始化stub代码:
auto rpcAddress =  QString("%1:%2").arg("xxx.xxx.xxx.xxx").arg(8888).toStdString();
	m_stub = ClientRPC::BusinessService::NewStub(grpc::CreateChannel(
		rpcAddress, grpc::InsecureChannelCredentials()));

客户端存根是建立在gRPC Channel上的,在gRPC官方文档有详细说明,但是这样的写法有坑,gRPC::CreateChannel的时候是否成功我们作为调用者是不清楚的,channel创建失败了,stub还能正常调用吗?显然不能,所以我们在创建失败的时候进行重试,幸好gRPC已经为我们考虑到了,采用这样的写法会很安全

gpr_timespec tm_out{ 3, 0, GPR_TIMESPAN };
auto Channel = grpc::CreateChannel(rpcAddress,grpc::InsecureChannelCredentials());
//在定义时间之类,连接失败后,WaitForConnected会一直重试
bool connected = Channel->WaitForConnected(tm_out);  
	if (connected)
	{
		LOG(INFO) << "连接成功";
	}
	else
	{
		LOG(INFO) << "连接失败";
	}

	m_stub = ClientRPC::BusinessService::NewStub(Channel);


  //在channel_interface.h中写的很清楚,GetState会一直重试

  /// Get the current channel state. If the channel is in IDLE and
  /// \a try_to_connect is set to true, try to connect.
  virtual grpc_connectivity_state GetState(bool try_to_connect) = 0;

好,写到这里你可能觉得stub一定能创建成功了吧?新打了一个安装包发到用户环境,发现stub任然创建失败,当时我怀疑是不是解析域名时错误了,在我看到grpc源码分析之域名解析鉴定了我的想法,文章的大致意思是,C++中gRPC域名解析可能有点问题,当然我们最快速的解决办法就是放弃域名,直接使用IP地址进行连接。

在使用了IP地址进行连接以后,发现gRPC和服务端建立起了TCP连接,那么我们的Func也能正常调用了

3.能不能复用gRPC::Channel 以及stub?

这是以前客户端遇到的问题,所有gRPC的请求都重新New一个stub出来调用,测试环境当然可以这样做,不过会造成大量的资源浪费,客户端会和服务端建立许多TCP连接,造成客户端已经服务器大量的Time_Wait状态,必须调整TCP回收策略,所以最好是在构造的时候就把stub创建好,要使用的时候进行复用,减少资源开销,这里讲的挺清楚c++ - 我应该共享gRPC存根或通道吗 (中文版)

你可能感兴趣的:(gRPC C++客户端 超时Deadline Exceeded)