- 客户端skd 使用gRPC作为通信协议,定时(大概是120s)向服务器发送pingServer 请求。服务端是80端口,如xxx:80.
问题:
发现客户端不断的端口重连服务器的。
使用netstat -antp
如图, 如标红的服务器地址连接是TIME_WAIT,后面有和服务器建立连接 ESTABLISHED。TIME_WAIT 状态表明是client 端主动断开了连接。这和我之前的认知有点冲突,gRPC 应该是长连接,为什么这里每次都断开呢,这样不就长了短连接了吗?而且客户端主动断开的,会不会是client端哪里有问题?带着疑问,在client 抓了一包,发现client 总是受到一个length为17的包,然后就开始FIN 包,走TCP 挥手的流程。使用WireShark 对tcpdump的结果查看,发现这个length17的包,是一个GOAWAY 包。
这个是HTTP2定义的一个“优雅”退出的机制。
这里有HTTP2 GOAWAY stream 包的说明。
HTTP2 GOAWAY 说明
根据之前的对gRPC的了解,gRPC client 会解析域名,然后会维护一个lb 负载均衡,这个应该是gRPC对idle 连接的管理。pingServer 的时间间隔是120s, 但是gRPC 认为中间是idle连接,所以通知client 关闭空闲连接?为了验证这个想法,修改了一下gRPC 的demo, 因为我们client 端使用是cpp 的gRPC 异步调用方式,所以更加gRPC 的异步demo, 写了一个简单访问服务器的async_client
server
# <-! coding=UTF-8 ->
import sys
import grpc
from concurrent import futures
import grpc_service_pb2
import grpc_service_pb2_grpc
import time
import commands
from concurrent.futures import ThreadPoolExecutor
import logging
reload(sys)
sys.setdefaultencoding('utf-8')
_HOST = 'localhost'
_PORT = '50051'
logging.basicConfig()
def write_hive_file(dir, str):
open(dir, 'a').write(str)
def handler(cmdstr):
cmd_arr = cmdstr.split(" ")
if len(cmd_arr) <= 2:
print "param error"
sys.exit(1)
cmd = ""
for i in range(0, len(cmd_arr)):
cmd = "%s %s" % (cmd, cmd_arr[i])
log = "[%s] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), cmd)
print log
time.sleep(5)
thread_pool = ThreadPoolExecutor(16)
class ServiceMain(grpc_service_pb2_grpc.RemoteCallServiceServicer):
def call(self, request, context):
cmdstr = str(object=request.cmdReqStr)
# 处理业务
print cmdstr
thread_pool.submit(handler, cmdstr)
return grpc_service_pb2.RemoteResponse(cmdRespStr="service handler you cmd:\"%s\"" % cmdstr)
def server():
grpcserver = grpc.server(futures.ThreadPoolExecutor(max_workers=4))
grpc_service_pb2_grpc.add_RemoteCallServiceServicer_to_server(ServiceMain(), grpcserver)
grpcserver.add_insecure_port(_HOST+":"+_PORT)
grpcserver.start()
print "start success"
#write_hive_file(_LOG_PATH, "server start success")
try:
while True:
time.sleep(5)
print "循环一次"
except KeyboardInterrupt:
grpcserver.stop(0)
if __name__ == '__main__':
server()
client
# <-! coding=UTF-8 ->
import grpc
import grpc_service_pb2
import grpc_service_pb2_grpc
_HOST = 'localhost'
_PORT = '50051'
def run():
conn = grpc.insecure_channel(_HOST + ':' + _PORT)
client = grpc_service_pb2_grpc.RemoteCallServiceStub(channel=conn)
response = client.call(grpc_service_pb2.RemoteRequest(cmdReqStr='hello,world!'))
print("received: " + response.cmdRespStr)
if __name__ == '__main__':
run()
接下来的时间很简单,运行一下。
使用netstat -natp 观察,可以重新。 async_client 也是断开,重连。
进一步调试发现,把发包的时间修改为10s 的时候,可以保持连接,大于10s基本上连接就会断开。
小结:
gRPC 管理连接的方式,默认情况下,大于10s没有数据发送,gRPC 就会认为是个idle 连接。server 端会给client 端发送一个GOAWAY 的包。client 收到这个包之后就会主动关闭连接。下次需要发包的时候,就会重新建立连接。