Having an asyncio Queue shared between normal sanic code and background task


#1

Hi, I’m new here and would like to ask a question regarding Sanic.

The problem I am facing is that I want to have a producer-consumer design where the consumer is running in the background and producer is running at the http end.

something like:

from sanic import Sanic, response
from asyncio import Queue

app = Sanic(__name__)
q = Queue()


@app.get("/")
async def place_something_on_the_q(request):
    await q.put("item 1")

    return response.json({})


async def consume(app):
    while 1:
        t = await q.get()
        print(t)


app.add_task(consume)

if __name__ == "__main__":
    app.run(host="localhost", port="4000")

The problem is that the background tasks runs in another loop. Is it possible to specify the same loop?


#2

When using app.run(), it will always create a new event loop. Try to use app.create_server() instead of app.run(). See the example.


#3

Another solution would be to use add_task as you started. This is how I would do it: How to use asyncio queues in Sanic?


#4

Thank you both for the responses.

They both work but what is the best?
Is there anything wrong with the following snippet?
Instead of adding a queue to the sanic object I have a global variable.

from sanic import Sanic, response
import asyncio
from signal import signal, SIGINT

app = Sanic(__name__)
q = asyncio.Queue()


@app.get("/")
async def place_something_on_the_q(request):
    await q.put("item 1")

    return response.json({})


async def consume():
    while 1:
        t = await q.get()
        print(t)


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    server = app.create_server(host="0.0.0.0", port=4000)
    task = loop.create_task(server)
    task = loop.create_task(consume())
    signal(SIGINT, lambda s, f: loop.stop())
    try:
        loop.run_forever()
    except:
        loop.stop()

#5

No, there is nothing wrong with your strategy. I generally prefer not to have global variables where I can avoid them, and the create_server method may not be as straightforward as app.run. But under the hood they are the same thing.