0Pricing
Python Academy · Урок

Шаблоны асинхронного ввода-вывода в продуктивной среде

Применяйте 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 — локальная установка не требуется.

Все уроки этого курса

  1. Корутины и цикл событий
  2. await, задачи и объединение результатов
  3. Асинхронные менеджеры контекста и итераторы
  4. Шаблоны асинхронного ввода-вывода в продуктивной среде
← Назад к Python Academy