Search code examples
pythondjangomultiprocessingpython-asyncio

How to parallelize requests in django script run from the command line?


I have a local Django project and some scripts that execute requests in parallel, but the requests are always executed synchronously.

Here is a sample script that demonstrates the issue:

import asyncio
import json

import requests
from asgiref.sync import async_to_sync


async def do_task(task):
    print(f"starting task {task}")
    response = requests.get("https://swapi.dev/api/people/1")
    response_json = json.loads(response.text)
    print(response_json)
    print(f"finished task {task}")


async def run():
    tasks = set()
    for i in range(5):
        task = asyncio.create_task(do_task(i))
        tasks.add(task)
        task.add_done_callback(tasks.discard)
    await asyncio.gather(*tasks)

# python manage.py shell < path/to/scripts/test.py
if __name__ in ("__main__", "django.core.management.commands.shell"):
    print("==start==")
    async_to_sync(run)()
    print("==done==")

The output for this script is:

==start==
starting task 0
{...} # response
finished task 0
starting task 1
{...} # response
finished task 1
starting task 2
{...} # response
finished task 2
starting task 3
{...} # response
finished task 3
starting task 4
{...} # response
finished task 4
==done==

I would expect to see something closer to this (all tasks started at the same time):

==start==
starting task 0
starting task 1
starting task 2
starting task 3
starting task 4
{...} # response
{...} # response
{...} # response
{...} # response
{...} # response
finished task 0
finished task 1
finished task 2
finished task 3
finished task 4
==done==

Solution

  • requests blocks the main thread, similar to using timer.sleep instead of asyncio.sleep.

    Using aiohttp solves the issue.

    Thanks @dirn for the help!

    import asyncio
    import json
    
    import aiohttp
    from asgiref.sync import async_to_sync
    
    
    async def do_task(task):
        print(f"starting task {task}")
        async with aiohttp.ClientSession() as session:
            async with session.get("https://swapi.dev/api/people/1") as response:
                response_json = json.loads(await response.text())
                print(response_json)
        print(f"finished task {task}")
    
    
    async def run():
        tasks = set()
        for i in range(10):
            task = asyncio.create_task(do_task(i))
            tasks.add(task)
            task.add_done_callback(tasks.discard)
        await asyncio.gather(*tasks)
    
    # python mainsite/manage.py shell < mainsite/funds/scripts/test.py
    if __name__ in ("__main__", "django.core.management.commands.shell"):
        print("==start==")
        async_to_sync(run)()
        print("==done==")