matejcik
matejcik

Reputation: 2072

asyncio: multiplexing messages over single websocket connection

I am using Python 3.6, asyncio and the websockets library. I am trying to build a client for a websocket-based service which works as follows:

The client can send JSON requests with a custom id, a method and some params. The service will reply with a JSON payload with the same id echoed, and data as a result of the method call.

I would like to have an abstraction on top of this device that would work sort of like this:

wsc = get_websocket_connection()

async def call_method(method, **params):
    packet = make_json_packet(method, params)
    await wsc.send(packet)
    resp = await wsc.recv()
    return decode_json_packet(resp)

async def working_code():
    separate_request = asyncio.ensure_future(call_method("quux"))

    first_result = await call_method("foo", x=1)
    second_result = await call_method("bar", y=first_result)
    print(second_result)

    return await separate_request

Now, I expect the separate_request to wait asynchronously while first_result and second_results are processed. But I have no guarantee that the wsc.recv() call will return the matching response; in fact, I have no guarantees that the service returns the responses in order of requests.

I can use the id field to disambiguate the responses. But how can I write the call_method() so that it manages the requests internally and resumes the "right" coroutine when the corresponding reply is received?

Upvotes: 1

Views: 868

Answers (1)

Sam Mason
Sam Mason

Reputation: 16213

when I've done this sort of thing before I've tended to split things out into two parts:

  1. "sending code" (can be multiple threads) this sets up where responses should go to (i.e. a dict of ids to functions or Futures), then sends the request and blocks for the response
  2. "receiving code" (probably one thread per socket) that monitors all inbound traffic and passes responses off to whichever code is interested in the id. this is also a sensible place to handle the socket being closed unexpectedly which should push an exception out as appropriate

this is probably a few hundred lines of code and pretty application specific…

Upvotes: 1

Related Questions