Шаблоны асинхронного ввода-вывода в продуктивной среде
Применяйте asyncio к HTTP-клиентам, очередям и практическим конвейерам обработки данных.
«Шаблоны асинхронного ввода-вывода в продуктивной среде» — бесплатный урок Python Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Python Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Python Academy содержит 4 уроков всего.
Рекомендации по циклу событий
Всегда используйте asyncio.run() как единственную точку входа. Не смешивайте синхронный и асинхронный код без необходимости и никогда не блокируйте цикл событий длительными вычислениями, нагружающими CPU.
import asyncio
async def main():
# Never: time.sleep() — blocks the event loop
# Always: await asyncio.sleep()
await asyncio.sleep(1)
asyncio.run(main())Пулы соединений
Повторно используйте соединения вместо открытия нового соединения для каждого запроса. Такие библиотеки, как asyncpg и aiohttp, предоставляют встроенные пулы.
# pip install asyncpg
import asyncpg, asyncio
async def main():
pool = await asyncpg.create_pool(
dsn="postgresql://user:pass@host/db",
min_size=5, max_size=20
)
async with pool.acquire() as conn:
row = await conn.fetchrow("SELECT 1")
await pool.close()Производитель и потребитель с asyncio.Queue
asyncio.Queue отделяет производителей от потребителей, работающих как параллельные задачи.
import asyncio
async def producer(q):
for i in range(5):
await q.put(i)
await asyncio.sleep(0.5)
await q.put(None) # sentinel
async def consumer(q):
while (item := await q.get()) is not None:
print(f"processing {item}")
q.task_done()
async def main():
q = asyncio.Queue()
await asyncio.gather(producer(q), consumer(q))
asyncio.run(main())Ограничение частоты с помощью семафора
Ограничивайте количество одновременно выполняемых исходящих запросов, чтобы не перегружать внешние API и не достигать ограничений частоты.
import asyncio, aiohttp
SEM = asyncio.Semaphore(10) # 10 concurrent requests max
async def fetch(session, url):
async with SEM:
async with session.get(url) as r:
return await r.json()
async def main(urls):
async with aiohttp.ClientSession() as s:
return await asyncio.gather(*[fetch(s, u) for u in urls])Повторные попытки с экспоненциальной задержкой
Оборачивайте операции, допускающие повторную попытку, в логику задержки, чтобы корректно обрабатывать временные сбои.
import asyncio
async def retry(coro_fn, retries=3, base=1):
for attempt in range(retries):
try:
return await coro_fn()
except Exception as e:
if attempt == retries - 1: raise
delay = base * 2 ** attempt
await asyncio.sleep(delay)Корректное завершение работы
Перехватывайте asyncio.CancelledError в длительно выполняющихся задачах и обрабатывайте сигналы OS для корректного завершения работы.
import asyncio, signal
async def server():
stop = asyncio.Event()
loop = asyncio.get_running_loop()
loop.add_signal_handler(signal.SIGINT, stop.set)
await stop.wait()
print("Shutting down gracefully")Потоковая передача ответов
Используйте асинхронные генераторы для потоковой передачи больших ответов без полной загрузки их в память.
import asyncio, aiohttp
async def stream(url):
async with aiohttp.ClientSession() as s:
async with s.get(url) as r:
async for chunk in r.content.iter_chunked(4096):
process(chunk)
async def process(chunk):
await asyncio.sleep(0)
print(len(chunk), "bytes")Фоновые задачи в FastAPI
BackgroundTasks в FastAPI ставит работу в очередь для выполнения после отправки ответа, сохраняя высокую скорость работы конечных точек.
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def send_email(to: str, body: str):
# heavy synchronous work
pass
@app.post("/register")
async def register(email: str, bg: BackgroundTasks):
bg.add_task(send_email, email, "Welcome!")
return {"status": "registered"}Шаблон контрольного сигнала
Запускайте периодическую фоновую задачу параллельно с основным циклом сервера с помощью create_task.
import asyncio
async def heartbeat():
while True:
print("alive")
await asyncio.sleep(30)
async def main():
asyncio.create_task(heartbeat())
await serve_forever()Профилирование асинхронного кода
Используйте yappi или py-spy для профилирования с поддержкой асинхронного кода. Включите режим отладки asyncio, чтобы получать предупреждения о медленных обратных вызовах.
# Enable debug to catch slow callbacks:
# asyncio.run(main(), debug=True)
# PYTHONASYNCIODEBUG=1 python server.py
# logs: Executing <Task coroutine=...> took 0.150 secondsКак избежать распространённых ошибок
Основные правила: никогда не вызывайте блокирующий код в цикле событий; всегда ожидайте сопрограммы; отменяйте задачи при завершении работы; используйте пулы соединений; ограничивайте параллелизм с помощью семафоров.
import asyncio
async def bad():
import time
time.sleep(2) # blocks the entire event loop!
async def good():
await asyncio.sleep(2) # yields controlБыстрая проверка
Какая примитивная конструкция asyncio отделяет производителей от потребителей, работающих как параллельные задачи?
Итоги
Асинхронные шаблоны для рабочих систем: используйте пулы соединений, управляйте параллелизмом с помощью Semaphore, реализуйте повторные попытки с задержкой, передавайте большие объёмы данных потоково, корректно завершайте работу по сигналам и запускайте периодические задачи с помощью create_task.
Часто задаваемые вопросы
Урок «Шаблоны асинхронного ввода-вывода в продуктивной среде» бесплатный?
Да — полный текст урока «Шаблоны асинхронного ввода-вывода в продуктивной среде» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Python Academy, подпишись на CoddyKit PRO. Курс Python Academy содержит 4 уроков всего.
Чему я научусь в уроке «Шаблоны асинхронного ввода-вывода в продуктивной среде»?
Применяйте asyncio к HTTP-клиентам, очередям и практическим конвейерам обработки данных. Ты практикуешь Python Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Python Academy?
Предыдущий опыт не требуется. Python Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Шаблоны асинхронного ввода-вывода в продуктивной среде»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Python Academy?
Да. Каждый урок Python Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Корутины и цикл событий
- await, задачи и объединение результатов
- Асинхронные менеджеры контекста и итераторы
- Шаблоны асинхронного ввода-вывода в продуктивной среде