Однако создается впечатление, что мои запросы не эффективно перекрывают время ожидания ввода-вывода. Когда я проверяю общее время, оно представляет собой примерно сумму времени отдельных запросов, а не время самого медленного запроса. Я знаю, что asyncio не обеспечивает истинного параллелизма, как это делают потоки или отдельные процессы (на стороне Python он по-прежнему однопоточный, поэтому для работы, связанной с процессором, нельзя обойти GIL), но для задач, связанных с сетью, я ожидаю значительного параллелизма.
Я использую Python 3.12.0.
/>Вот код, который я использую. Я заменил свой внутренний API общедоступными заполнителями, но в моих собственных службах такое же поведение сохраняется.
Код: Выделить всё
import asyncio
import aiohttp
import time
async def fetch_data(session, url):
"""
Fetches data from a given URL using aiohttp.
Includes print statements to track apparent start/finish times.
"""
start_req_time = time.perf_counter()
print(f"[{time.time():.2f}] Starting request for: {url}")
try:
async with session.get(url) as response:
response.raise_for_status() # Raise an exception for bad status codes
data = await response.json() # Or .text(), depending on expected response
end_req_time = time.perf_counter()
print(f"[{time.time():.2f}] Finished request for: {url} in {end_req_time - start_req_time:.4f}s")
return {"url": url, "status": response.status, "data_length": len(str(data))}
except aiohttp.ClientError as e:
print(f"[{time.time():.2f}] Error fetching {url}: {e}")
return {"url": url, "error": str(e)}
async def main():
# Using a list of public JSON placeholder URLs for demonstration
# In my real app, these are different endpoints of my own API
urls = [
"https://jsonplaceholder.typicode.com/todos/1",
"https://jsonplaceholder.typicode.com/todos/2",
"https://jsonplaceholder.typicode.com/posts/1",
"https://jsonplaceholder.typicode.com/users/1",
"https://jsonplaceholder.typicode.com/comments/1",
]
total_start_time = time.perf_counter()
async with aiohttp.ClientSession() as session:
tasks = []
for url in urls:
tasks.append(fetch_data(session, url))
# This is where I expect the I/O waits of tasks to overlap
results = await asyncio.gather(*tasks)
total_end_time = time.perf_counter()
print(f"\n[{time.time():.2f}] All tasks completed in {total_end_time - total_start_time:.4f} seconds.")
print("\n--- Results ---")
for result in results:
print(result)
if __name__ == "__main__":
asyncio.run(main())
Код: Выделить всё
[1678886400.10] Starting request for: https://jsonplaceholder.typicode.com/todos/1
[1678886400.25] Finished request for: https://jsonplaceholder.typicode.com/todos/1 in 0.1500s
[1678886400.25] Starting request for: https://jsonplaceholder.typicode.com/todos/2
[1678886400.40] Finished request for: https://jsonplaceholder.typicode.com/todos/2 in 0.1500s
[1678886400.40] Starting request for: https://jsonplaceholder.typicode.com/posts/1
[1678886400.55] Finished request for: https://jsonplaceholder.typicode.com/posts/1 in 0.1500s
... and so on ...
[1678886400.80] All tasks completed in 0.7000 seconds.
Код: Выделить всё
0.7000 secondsКод: Выделить всё
5 * ~0.15s = ~0.75sЧто мне здесь не хватает? Есть ли какая-то общая ошибка с ClientSession aiohttp или asyncio.gather, которая приводит к такой сериализации для сетевых задач, даже с await? Может ли это быть связано с разрешением DNS, ограничениями TCP-соединений (локальными или на сервере) или чем-то в настройке ClientSession?
Я проверил:
- Версия Python (3.12.0)
- версия (
Код: Выделить всё
aiohttpпоказывает aiohttp==3.9.3)Код: Выделить всё
pip list - В моей функции fetch_data нет очевидного ожидания.
Подробнее здесь: https://stackoverflow.com/questions/798 ... ed-for-i-o