Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding async await in python socket io / aiohttp server

I am trying to setup an socket.io server using python-socketio. Here is a minimal working example:

import asyncio
from aiohttp import web
import socketio
import random

sio = socketio.AsyncServer(async_mode='aiohttp')
app = web.Application()
sio.attach(app)

@sio.on('connect')
def connect(sid, environ):
    print("connected: ", sid)

@sio.on('sendText')
async def message(sid, data):
    print("message ", data)
    # await asyncio.sleep(1 * random.random())
    # print('waited', data)

@sio.on('disconnect')
def disconnect(sid):
    print('disconnect ', sid)

if __name__ == '__main__':
    web.run_app(app, host='0.0.0.0', port=8080)

This runs fine, and I can execute (here in node.js) for instance

const io = require('socket.io-client');
const socket = io('ws://localhost:8080');
socket.emit('sendText', 'hey 1')
socket.emit('sendText', 'hey 2')
socket.emit('sendText', 'hey 3')

If I run the server and run the node script above I get server-side

connected: c1e687f0e2724b339fcdbefdb5aaa8f8

message hey 1

message hey 2

message hey 3

However, if I uncomment the lines with await sleep in the code, I only receive the first message:

connected: 816fb6700f5143f7875b20a252c65f33

message hey 1

waited hey 1

I don't understand why the next messages are not appearing. Can only one instance of async def message run at the same time? Or why?

I am sure that I am not understanding something very fundamental about how this works. I would be very grateful if someone could point out what I am not understanding.

like image 922
Julius Avatar asked Jan 12 '18 15:01

Julius


People also ask

How does Python async await work?

An async function uses the await keyword to denote a coroutine. When using the await keyword, coroutines release the flow of control back to the event loop. To run a coroutine, we need to schedule it on the event loop. After scheduling, coroutines are wrapped in Tasks as a Future object.

Does await block Python?

In Python, we need an await keyword before each coroutine object to have it called by the event loop. But when we put await , it makes the call blocking. It follows that we end up doing the same thing as we do in the blocking fashion.

How do you await a function in Python?

To run an async function (coroutine) you have to call it using an Event Loop. Event Loops: You can think of Event Loop as functions to run asynchronous tasks and callbacks, perform network IO operations, and run subprocesses. Example 1: Event Loop example to run async Function to run a single async function: Python3.

What is Asyncio run?

asyncio. run(main()) asyncio is a library to write concurrent code using the async/await syntax. asyncio is used as a foundation for multiple Python asynchronous frameworks that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc.


1 Answers

I'm the author of the python-socketio package. There are two problems here, I think. I can answer your question:

Can only one instance of async def message run at the same time? Or why?

My Socket.IO server serializes the events that are received from a given client. So for example, if client A sends an event that runs for one minute, any additional events sent by A during that minute will be queued, waiting for the first event to complete first. If client B sends an event during that minute, it will be handled immediately. The reason why events from a client are artificially serialized is to prevent race conditions or other side effects from occurring as a result of two or more handlers for the same client running in parallel. This serialization of events can be turned off, with the async_handlers option:

sio = socketio.AsyncServer(async_mode='aiohttp', async_handlers=True)

Using aiohttp 2.3.7 and async_handlers=True your three events are received at more or less the same time, and then all handlers wait in parallel during their sleep periods.

Unfortunately this does not explain the 2nd and 3rd events never reaching the server. I have verified that these events are properly queued and executed in sequence with aiohttp 2.2.5, but this breaks with 2.3.0 all the way to 2.3.7. My current theory is that a change that was introduced in 2.3.0 is causing these messages that arrive while the task is sleeping to get dropped, but haven't found why that happens yet.

like image 192
Miguel Avatar answered Oct 15 '22 12:10

Miguel