Как распараллелить запросы в скрипте django, запущенном из командной строки?
У меня есть локальный проект Django и несколько скриптов, которые выполняют запросы параллельно, но запросы всегда выполняются синхронно.
Вот пример сценария, который демонстрирует проблему:
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==")
Вывод для этого сценария следующий:
==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==
Я бы ожидал увидеть что-то близкое к этому (все задачи запускаются в одно и то же время):
==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==
запросы блокируют основной поток, аналогично использованию timer.sleep
вместо asyncio.sleep
.
Использование aiohttp решает проблему.
Спасибо @dirn за помощь!
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==")