- 客户端skd 使用gRPC作为通信协议,定时(大概是120s)向服务器发送pingServer 请求。服务端是80端口,如xxx:80.
问题:
发现客户端不断的端口重连服务器的。
使用netstat -antp
image.png如图, 如标红的服务器地址连接是TIME_WAIT,后面有和服务器建立连接 ESTABLISHED。TIME_WAIT 状态表明是client 端主动断开了连接。这和我之前的认知有点冲突,gRPC 应该是长连接,为什么这里每次都断开呢,这样不就长了短连接了吗?而且客户端主动断开的,会不会是client端哪里有问题?带着疑问,在client 抓了一包,发现client 总是受到一个length为17的包,然后就开始FIN 包,走TCP 挥手的流程。使用WireShark 对tcpdump的结果查看,发现这个length17的包,是一个GOAWAY 包。
image.png
-
这个是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 收到这个包之后就会主动关闭连接。下次需要发包的时候,就会重新建立连接。
网友评论