gRPC Python 服务器通过函数调用与客户端通信

时间:2021-05-10 06:20:54

标签: grpc-python

假设我正在使用带有以下 .proto 的 gRPC 构建一个简单的聊天应用程序:

service Chat {
    rpc SendChat(Message) returns (Void);
    rpc SubscribeToChats(Void) returns (stream Message);
}

message Void {}
message Message {string text = 1;}

我经常看到用 Python 实现的服务程序(在示例中)是这样的:

class Servicer(ChatServicer):
    def __init__(self):
        self.messages = []

    def SendChat(self, request, context):
        self.messages.append(request.text)
        return Void()

    def SubscribeToChats(self, request, context):
        while True:
            if len(self.messages) > 0:
                yield Message(text=self.messages.pop())    

虽然这有效,但生成一个无限循环来不断检查每个连接的客户端的条件似乎效率很低。最好使用这样的方式,即在消息传入时立即触发发送,并且不需要在条件下进行任何持续轮询:

class Servicer(ChatServicer):
    def __init__(self):
        self.listeners = []

    def SendChat(self, request, context):
        for listener in self.listeners:
            listener(Message(request.text))
        return Void()

    def SubscribeToChats(self, request, context, callback):
        self.listeners.append(callback)    

但是,我似乎找不到使用 gRPC 执行此类操作的方法。

我有以下问题:

  1. 对于这种情况,无限循环效率低下的说法是否正确?或者是否有我不知道的后台优化?
  2. 有没有什么有效的方法可以实现类似于我上面首选的解决方案?这似乎是一个相当常见的用例,所以我确定我遗漏了一些东西。

提前致谢!

1 个答案:

答案 0 :(得分:0)

我想出了一个有效的方法来做到这一点。关键是使用AsyncIO API。现在我的 SubscribeToChats 函数可以是一个异步生成器,这让事情变得更容易了。

现在我可以使用像 asyncio Queue 这样的东西,我的函数可以在 while 循环中等待它。类似这样:

class Servicer(ChatServicer):
    def __init__(self):
        self.queue = asyncio.Queue()

    async def SendChat(self, request, context):
        await self.queue.put(request.text)
        return Void()

    async def SubscribeToChats(self, request, context):
        while True:
            yield Message(text=await self.queue.get())