← Назад к курсу

System Design с применением Python


Цель: Понимать принципы проектирования масштабируемых систем и уметь применять их с использованием экосистемы Python.

Длительность: 1.5 - 2 часа


Часть 1: Введение (10 минут)

Ключевые вопросы:

  1. Что такое System Design? Проектирование архитектуры системы (компоненты, связи, технологии) для удовлетворения функциональных и нефункциональных требований (масштабируемость, надежность, производительность, сопровождаемость).
  2. Почему Python? Python — язык высокого уровня с богатой экосистемой, идеален для быстрого прототипирования и построения бэкенд-сервисов, обработки данных, ML-пайплайнов. Важно понимать его сильные и слабые стороны в контексте больших систем.

Типичный процесс проектирования:

  1. Сбор требований (Functional / Non-Functional).
  2. Оценка масштаба (Traffic, Storage, Bandwidth).
  3. Высокоуровневая архитектура (Схема, основные компоненты).
  4. Детальное проектирование (API, БД, кэши, асинхронность и т.д.).
  5. Идентификация узких мест и их устранение.
  6. Дополнительные вопросы (мониторинг, деплой, отказоустойчивость).

Часть 2: Разбор кейса: Проектирование сокращателя URL (URL Shortener) (70 минут)

Шаг 1: Требования и оценки (10 мин)

  • Функциональные: Сократить URL, перенаправить по короткой ссылке, настроить срок жизни, кастомные ссылки.
  • Нефункциональные: Высокая доступность, минимальная задержка, предсказуемость.
  • Оценки (Back-of-the-envelope):
    • Пиковый RPS (запросов на чтение): 100K/s (в основном редиректы).
    • Соотношение записи/чтения: 1:100.
    • Хранение ссылок 5 лет: (100K / 100) * 3600 * 24 * 365 * 5 ≈ 1.6 млрд записей.
    • Объем данных: 1.6 млрд * 500 байт ≈ 800 ГБ.

Шаг 2: Высокоуровневая архитектура (API, БД) (15 мин)

# Пример API с использованием FastAPI (псевдокод)
from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.post("/shorten")
async def shorten_url(long_url: str, custom_alias: str = None):
    # 1. Валидация URL
    # 2. Генерация или проверка custom_alias (например, "my_cool_link")
    # 3. Сохранение в БД: id (short_key) -> long_url, created_at, expires_at
    # 4. Возврат короткой ссылки: https://short.co/{short_key}
    pass

@app.get("/{short_key}")
async def redirect(short_key: str):
    # 1. Поиск long_url по short_key в... кэше? БД?
    # 2. 301/302 редирект
    # 3. Логирование клика (асинхронно, через очередь)
    pass
  • Выбор БД: Нужен быстрый lookup по ключу. Redis (кэш) + PostgreSQL или DynamoDB/Cassandra (постоянное хранение). Для Python хороши клиенты redis-py, asyncpg, boto3.

Шаг 3: Генерация короткого ключа (10 мин)

  • Варианты: Hash (MD5, Base62), Random, Distributed Sequence (Snowflake ID).
  • Проблема коллизий и глобальной уникальности. Python-реализация:
import hashlib
import base64

def generate_short_key(long_url: str, salt: str = "") -> str:
    hash_bytes = hashlib.md5((long_url + salt).encode()).digest()
    # Берем первые 6 байт хэша, кодируем в base62 (A-Za-z0-9)
    short_key = base64.urlsafe_b64encode(hash_bytes[:6]).decode().rstrip("=")
    return short_key

Шаг 4: Масштабирование и узкие места (20 мин)

  1. Кэширование редиректов: 99% трафика — чтение. Redis как LRU-кэш перед БД. Библиотеки: redis-py.
    import redis
    cache = redis.Redis(host='localhost', decode_responses=True)
    long_url = cache.get(short_key)
    if not long_url:
        long_url = db.get(short_key)  # Чтение из основной БД
        cache.setex(short_key, 3600, long_url)  # TTL 1 час
    
  2. Асинхронность: Используем async/await (FastAPI, aiohttp) для обработки тысяч одновременных соединений без блокировок на I/O (ожидание БД, кэша).
  3. Очереди задач для фоновой работы: Логирование кликов, очистка просроченных ссылок. Celery + RabbitMQ/Redis или RQ.
    # tasks.py с Celery
    @celery.task
    def log_click(short_key: str, user_agent: str):
        # Асинхронно пишем в ClickHouse/Postgres
        pass
    
  4. Шардирование БД: При 1.6 млрд записей одна БД не справится. Шардируем по short_key. В Python нужно продумать логику роутинга к разным кластерам БД.

Шаг 5: Детали (15 мин)

  • Rate Limiting: Защита API от спама. Библиотеки: slowapi, fastapi-limiter.
  • Мониторинг и логирование: prometheus-client, opentelemetry, структурированные логи с structlog + json-formatter.
  • Конфигурация и секреты: pydantic-settings, python-dotenv.
  • Контейнеризация: Docker, Docker Compose. Образы на основе python:3.12-slim.

Часть 3: Паттерны и антипаттерны в Python для больших систем (15 минут)

Паттерны:

  1. Repository/Service Layer: Отделение логики от доступа к данным.
  2. Dependency Injection: Упрощение тестирования (библиотеки dependency-injector, FastAPI встроенный DI).
  3. Circuit Breaker: Защита от каскадных сбоев (pybreaker).
  4. Connection Pooling: Для БД (asyncpg) и HTTP-клиентов (aiohttp).

Антипаттерны:

  1. Глобальные объекты (БД, кэш) в модуле: Усложняет тестирование и управление состоянием.
  2. Блокирующие вызовы в event-loop: Использование requests вместо aiohttp в асинхронном приложении.
  3. Игнорирование GIL: Для CPU-bound задач (обработка видео, сложные вычисления) использовать multiprocessing, concurrent.futures.ProcessPoolExecutor или вынос в отдельные сервисы на других языках.
  4. Самописные решения вместо проверенных библиотек (например, для кэширования, фоновых задач).

Часть 4: Практическое задание / Домашняя работа (5 минут)

Задание: Спроектировать систему сбора метрик (как мониторинг) с агентами на многих серверах.

  • Требования: Агенты отправляют метрики (CPU, память) каждые 10 сек. Центральный сервер хранит данные 30 дней, предоставляет API для построения графиков.
  • Вопросы для обсуждения:
    1. Какой протокол использовать между агентом и сервером? (HTTP, gRPC, UDP).
    2. Какую БД выбрать для хранения временных рядов? (InfluxDB, TimescaleDB). Какие есть Python-клиенты?
    3. Как обрабатывать пиковую нагрузку с тысяч агентов? (Очереди, буферизация, batching).
    4. Как обеспечить доступность данных? Репликация БД.

Ресурсы для самостоятельного изучения:

  1. Книги: "Designing Data-Intensive Applications" (Martin Kleppmann).
  2. Статьи: Блог High Scalability, архитектурные кейсы от Netflix, Uber.
  3. Python-специфика: Документация asyncio, блоги компаний, использующих Python в high-load (YouTube, Instagram, Spotify).
  4. Курсы: "Grokking the System Design Interview", "System Design Interview" на YouTube.

Итог: Python — мощный инструмент для построения сложных распределенных систем, но его эффективность напрямую зависит от понимания архитектурных принципов и грамотного выбора инструментов из его богатой экосистемы. Ключ — компенсировать его "слабые" стороны (производительность CPU-bound задач) правильной архитектурой (микросервисы, очереди, выбор подходящих БД).