首页 > 解决方案 > Python GRPC 如何正确重用通道

问题描述

我正在编写一个 GRPC python 客户端来与 GRPC 服务器进行通信,如下所示。

class GrpcClient:
    """
    Class to send request to Grpc server.
    """

    def __init__(self, host: str, port: int):
        self._server_address = host + ':' + str(port)
        with grpc.insecure_channel(self._server_address) as channel:
            self._stub = MyServerStub(channel)

    def invoke_method(self, request):
        response = self._stub.process(request)
        logging.info("response received: " + str(response))

这会导致错误ValueError: Cannot invoke RPC on closed channel!由于通道在int上关闭,我修改如下

    def __init__(self, host: str, port: int):
        self._server_address = host + ':' + str(port)
        channel = grpc.insecure_channel(self._server_address)
        self._stub = MyServerStub(channel)

现在通道和存根都被重用了,但我担心通道可能没有关闭,这可能会导致内存泄漏

为了防止它,我尝试打开通道并在每个请求上创建存根,这很昂贵。

任何人都可以建议一种重用通道和存根而不会泄漏的方法吗?

标签: pythonpython-3.xmemory-leaksgrpcgrpc-python

解决方案


有两种方法可以解决这个问题,它们不一定是相互排斥的。第一个是将通道的close方法代理到您的类并要求您的类的用户调用它。看起来像这样:

class GrpcClient:
  def __init_(self, ...):
    self._channel = grpc.insecure_channel(...)
    self._stub = MyServerStub(self._channel)

  def close(self):
    self._channel.close()

为了让这个 API 更简洁,你可以提供一个上下文管理器 API,类似于grpc.Channel

class GrpcClient:
  ...
  def __enter__(self):
    return self
  
  def __exit_(self, ...):
    self.close()

然后你可以使用你的类

with GrpcClient(...) as client:
  for task in tasks:
    do_work(client, task)

过去人们曾尝试使用__del__自动调用此类清理方法,但我们发现 CPython 提供的关于何时以及是否调用此方法的保证太弱,无法提供任何类似于 C++ RAII 的东西,所以我们就离开了使用我上面概述的手动选项。


推荐阅读