简析gRPC client 连接管理

  • 客户端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 收到这个包之后就会主动关闭连接。下次需要发包的时候,就会重新建立连接。

你可能感兴趣的:(简析gRPC client 连接管理)