6

I must be doing something wrong... my gRPC server is implemented in node.js:

function handler(call, callback) {
   console.log('Received request at ' + Date.now());
   setTimeout(() => {
      callback({ message: 'Done and done' });
   }, 100);
}

If I call it 1,000 in Node, I get 1,000 responses in about 100ms:

const resps = [];
for (let i = 0; i < 1000; i += 1) {
    client.doit({ data }, (err, resp) => {
      resps.push(resp);
      if (resps.length === 1000) {
        onDone();
      }
    });
}

However, calling the server from Python using the service.future I can see the server only receiving a request after the previous one has returned:

for _ in range(1000):
    message = Message(data=data)
    resp = client.doit.future(message)
    resp = resp.result()
    resps.append(resp)

I get that Node's IO paradigm is different (everything is async; event loop; etc.), and the Python example above blocks on out.result(), but my question is: can could I change/optimize the Python client so it can make multiple calls to my server without waiting for the first one to return?

rodrigo-silveira
  • 12,607
  • 11
  • 69
  • 123

1 Answers1

6

You can make asynchronous unary calls in python like so:

class RpcHandler:
    def rpc_async_req(self, stub):
        def process_response(future):
            duck.quack(future.result().quackMsg)

        duck = Duck()
        call_future = stub.Quack.future(pb2.QuackRequest(quackTimes=5)) #non-blocking call
        call_future.add_done_callback(process_response) #non-blocking call
        print('sent request, we could do other stuff or wait, lets wait this time. . .')
        time.sleep(12) #the main thread would drop out here with no results if I don't sleep
        print('exiting')

class Duck:
    def quack(self, msg):
        print(msg)


def main():
    channel = grpc.insecure_channel('localhost:12345')
    stub = pb2_grpc.DuckServiceStub(channel)
    rpc_handler = RpcHandler()
    rpc_handler.rpc_async_req(stub=stub)

if __name__ == '__main__':
    main()

proto

syntax = "proto3";

package asynch;

service DuckService {
    rpc Quack (QuackRequest) returns (QuackResponse);
}

message QuackRequest {
    int32 quackTimes = 1;
}

message QuackResponse {
    string quackMsg = 1;
}
Kevin S
  • 930
  • 10
  • 19
  • Is there a way to use await instead of sleep? – Diego Apr 14 '20 at 08:49
  • I think the gRPC framework is using threads not an event loop, so you do not need to await, you could add an event to the response processing and await this event. https://docs.python.org/3/library/asyncio-sync.html#asyncio.Event – Tomasz Swider Sep 09 '20 at 09:50
  • I've successfully wrapped the custom `grpc.Future` to be able to `await` it as described here https://stackoverflow.com/questions/49350346/how-to-wrap-custom-future-to-use-with-asyncio-in-python – Prinzhorn Feb 03 '22 at 11:02