Масштабирование REST API с пагинацией и rate limit | Вопросы для собеседования | Skilio
Масштабирование REST API с пагинацией и rate limit
Вопрос:

Как бы вы спроектировали масштабируемый REST API на Python?

Давайте заодно рассмотрим реализацию rate limit и пагинации для улучшения производительности и пользовательского опыта.

Подсказки:

  1. Подумайте о использовании FastAPI или Django REST Framework в качестве основного фреймворка
  2. Подумайте о том, как справиться с растущей нагрузкой посредством горизонтального масштабирования.
  3. Пагинацию можно реализовать с помощью подхода на основе смещения или подхода на основе курсора.

Выше ожиданий:

  • Понимание асинхронного программирования с FastAPI
  • Для ограничения скорости запросов исследуйте алгоритмы "текущего ведра" (leaky bucket) или алгоритмы на основе фиксированного окна (fixed window)
  • Понимание использования очередей сообщений для оффлоадинга задач
  • Знание техник шардирования баз данных для горизонтального масштабирования
Ответ:

Выбор фреймворка

Выберите FastAPI для компактных и быстрых микросервисов, его преимщуества:

  • Основан на Starlette и Pydantic
  • Встроенная поддержка асихнронных операций
  • Автоматическая генерация документации и спецификации OpenAPI
  • Валидация типов с помощью Python type hints
  • Более высокая производительность, чем Django REST Framework

В качестве альтернативы используйте Django REST Framework, когда:

  • Вам нужен зрелая экосистема с большим количеством встроенных функций
  • Требуются их коробки нужна аутентификация, пермиссии и готовые views
  • Проект уже использует Django
  • Команда разработки более знакома с Django

Архитектура для горизонтального масштабирования

  1. Создание stateless сервисов:

    • Храните данные сессий в Redis или других распределённых кэшах
    • Избегайте локального хранения файлов или данных в оперативной памяти, которые не синхронизируются между экземплярами
  2. Балансировщики нагрузки:

    • Используйте Traefik, Nginx или HAProxy в качестве обратного прокси-сервера
    • Настройте алгоритмы round-robin или least-connections
    • Включите проверки состояния (health checks) для маршрутизации трафика только на здоровые экземпляры
  3. Контейнеризация приложения:

    • Упакуйте API в контейнеры Docker
    • Используйте Kubernetes для оркестрации и автоматического масштабирования
    • Определите ограничения ресурсов и политики автоматического масштабирования
# Пример Kubernetes HPA (Horizontal Pod Autoscaler)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-deployment
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Стратегии пагинации

Реализуйте пагинацию для управления большими наборами данных:

  1. Пагинация на основе смещения:

    • Параметры: limit и offset
    • Легко реализуется
    • Хорошо работает для статических данных
    • Производительность снижается при больших смещениях
    • Пример: /api/items?limit=20&offset=40
  2. Пагинация на основе курсора (ID закодирован в курсоре):

    • Использует указатель на последнюю полученную запись
    • Лучшая производительность для больших наборов данных
    • Умело обрабатывает вставки/удаления
    • Более сложно реализовать
    • Пример: /api/items?limit=20&cursor=eyJpZCI6MTAwfQ==
  3. Пагинация на основе набора ключей:

    • Использует значения последней записи в качестве ссылки
    • Хорошая производительность и согласованность
    • Пример: /api/items?limit=20&created_after=2023-01-15T12:00:00Z

Возвращайте метаданные пагинации в ответах:

{
  "items": [...],
  "pagination": {
    "total_items": 1543,
    "page_size": 20,
    "current_page": 3,
    "total_pages": 78,
    "next": "/api/items?page=4&page_size=20",
    "previous": "/api/items?page=2&page_size=20"
  }
}

Асинхронная обработка

Реализуйте асинхронные паттерны для повышения производительности:

  1. Используйте асинхронные функции FastAPI:
    • Эффективно обрабатывайте конкурентные запросы
    • Поддерживает отзывчивость API во время операций ввода-вывода
    • Снижайте потребление ресурсов под нагрузкой
@app.get("/items/{item_id}")
async def read_item(item_id: int):
    result = await database.fetch_one(f"SELECT * FROM items WHERE id = {item_id}")
    return result
  1. Передайте ресурсоёмкую обработку через очереди сообщений:
    • Используйте Celery с RabbitMQ или Redis
    • Обрабатывайте ресурсоёмкие задачи асинхронно
    • Возвращайте HTTP ответ с кодом 202 Accepted с ID задачи для отслеживания
    • Реализуйте вебхуки или опрос (поллинг) для уведомления о результате

Реализация ограничения скорости (rate limit)

Реализуйте ограничение скорости для защиты API от злоупотреблений и некотролируемых перегрузок:

  1. Алгоритм Token Bucket:

    • Распределяет токены с фиксированной скоростью на каждого клиента
    • Каждый запрос потребляет токен
    • При исчерпании токенов запросы отклоняются
    • Позволяет кратковременные всплески трафика в пределах лимитов
  2. Алгоритм текущего ведра (Leaky Bucket)

    • Моделирует обработку запросов как протекающую воду через ведро с дыркой
    • Обрабатывает запросы в соответствии со скоростью поступления
    • Избыточные запросы либо ставятся в очередь, либо отбрасываются, когда ведро (очередь) заполнено
    • Сглаживает скачки трафика и обеспечивает равномерную обработку
  3. Алгоритм Fixed Window:

    • Считает запросы в фиксированные временные интервалы (например, в минуту)
    • Сбрасывает счётчик в конце каждого интервала
    • Проще, но потенциально допускает всплески трафика на границах интервалов
  4. Алгоритм Sliding Window:

    • Объединяет аспекты алгоритма Fixed Window с скользящим временным интервалом
    • Более точное ограничение скорости без пиковых значений на границах интервалов

Подходы к реализации:

  • Используйте Redis для отслеживания лимитов скорости на нескольких экземплярах
  • Применяйте ограничения на основе IP-адреса, API-ключа или идентификатора пользователя
  • Возвращайте код состояния 429 (Too Many Requests) при достижении лимита
  • Включайте информацию об ограничениях скорости в заголовки ответов:
    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • X-RateLimit-Reset
# Пример ограничения скорости FastAPI с Redis
from fastapi import FastAPI, Depends, HTTPException
import redis
import time

app = FastAPI()
redis_client = redis.Redis(host='redis', port=6379, db=0)

async def rate_limit(user_id: str, limit: int = 100, window: int = 3600):
    current = redis_client.get(f"rate_limit:{user_id}")
    if current is None:
        redis_client.set(f"rate_limit:{user_id}", 1, ex=window)
        return True
    
    if int(current) >= limit:
        raise HTTPException(status_code=429, detail="Rate limit exceeded")
    
    redis_client.incr(f"rate_limit:{user_id}")
    return True

@app.get("/items/")
async def read_items(user_id: str, _: bool = Depends(rate_limit)):
    return {"items": ["item1", "item2"]}

Масштабирование базы данных

  1. Читающие реплики:

    • Прямые запросы чтения к репликам
    • Записи выполняются на главный хост кластера БД
    • Распределяет читающую нагрузку на другие хосты
  2. Шардирование базы данных:

    • Разделение данных по нескольким экземплярам базы данных
    • Шардирование по идентификатору клиента, географическому региону или другим критериям
    • Реализация слоя маршрутизации для направления запросов к соответствующему шарду
  3. Пулы подключений:

    • Используйте SQLAlchemy или пулы подключений Django
    • Повторное использование подключений к базе данных для уменьшения нагрузки
    • Настройте размер пула в зависимости от ресурсов экземпляра

Мониторинг и наблюдаемость

  • Используйте Prometheus для сбора метрик
  • Настройте панели Grafana для визуализации
  • Реализуйте распределённое трассировку с Jaeger или OpenTelemetry
  • Добавьте структурированный лог с идентификаторами запросов

Стратегия кэширования

Реализуйте несколько слоёв кэширования:

  • Кэширование ответов с Redis или аналогичной распределённой БД
  • Кэширование запросов к базе данных
  • CDN для статических ресурсов
  • Добавьте заголовки Cache-Control
0
Python Средний Опубликовано
© Skilio, 2025
Условия использования
Политика конфиденциальности
Мы используем файлы cookie, для персонализации сервисов и повышения удобства пользования сайтом. Если вы не согласны на их использование, поменяйте настройки браузера.