JavaRush /Курсы /Модуль 4: FastAPI /Кэширование сессий пользователей с Redis

Кэширование сессий пользователей с Redis

Модуль 4: FastAPI
9 уровень , 5 лекция
Открыта

В этой лекции мы наконец-то подойдем к тому, что делают почти все современные крупные веб-приложения — сохраним пользовательские сессии в Redis. Как вы уже догадались, Redis здесь выступит как быстрый и удобный инструмент для хранения временных данных.

Пользовательская сессия — это как билет в кино: она подтверждает, что пользователь (или система) уже "законно вошел" в наше приложение. Сессия может содержать идентификатор пользователя, токен авторизации, временную метку и другие данные. Зачем их хранить? Чтобы пользователь мог продолжить работу в приложении даже после закрытия браузера или перезагрузки страницы.

Однако если хранить сессии в базе данных (например, PostgreSQL), это может быстро стать бутылочным горлышком, ведь пользователи запрашивают десятки, если не сотни сессий каждую секунду. Redis же, будучи быстрой in-memory базой данных, идеально подходит для этой задачи.

Преимущества кэширования сессий в Redis:

  • Мгновенная скорость: Redis хранит данные в оперативной памяти, что делает доступ к сессиям невероятно быстрым.
  • Устойчивость: Redis поддерживает репликацию и дампирование данных, минимизируя риск потери сессий.
  • Гибкость: вы можете настраивать TTL для сессий, чтобы они автоматически удалялись через определенное время.
  • Обновление данных "на лету": хотите отслеживать активность пользователей? Вы легко можете обновлять данные сессий без лишних задержек.

Давайте разберем сессии через Redis как простую последовательность действий:

  1. Инициализация сессии: когда пользователь логинится, создается новая сессия, и её идентификатор сохраняется в Redis вместе с данными (например, ID пользователя).
  2. Чтение сессии: при последующих запросах идентификатор сессии используется для извлечения данных из Redis.
  3. Обновление TTL: TTL (время жизни) можно обновлять каждый раз, когда пользователь выполняет запрос, чтобы его сессия "не истекла" в процессе работы.
  4. Удаление сессии: как только пользователь разлогинился, сессия удаляется.

Установка и настройка окружения

У вас Redis уже должен быть установлен с предыдущих лекций. Но если вдруг вы пропустили, выполните:


sudo apt update
sudo apt install redis

Или запустите Redis через Docker:


docker run --name redis -p 6379:6379 -d redis

Убедитесь, что redis-py установлена:


pip install redis

Для асинхронного взаимодействия нам также понадобится aioredis:


pip install aioredis

Реализация кэширования сессий в FastAPI

Создадим клиент для работы с Redis. В нашем случае мы будем использовать асинхронный клиент на основе aioredis.


import aioredis
from fastapi import FastAPI

app = FastAPI()

# Подключение к Redis
redis = aioredis.from_url("redis://localhost", decode_responses=True)

Здесь decode_responses=True позволит нам работать с текстовыми данными вместо байтов.

Теперь, когда пользователь логинится, мы будем создавать запись о сессии в Redis.


from fastapi import HTTPException
from uuid import uuid4

@app.post("/login")
async def login(username: str):
    # (Пример простой авторизации, без проверки пароля)
    if username not in ["user1", "user2"]:
        raise HTTPException(status_code=401, detail="Invalid user")

    # Генерация уникального идентификатора сессии
    session_id = str(uuid4())

    # Сохраняем сессию в Redis
    await redis.set(f"session:{session_id}", username, ex=3600)  # TTL = 1 час

    return {"session_id": session_id}

Какие тут магические вещи происходят:

  • Уникальный идентификатор сессии генерируется через uuid4(). Это ваша "голопузая" защита от повторяющихся сессий.
  • Мы сохраняем сессию с ключом формата session:{session_id}.
  • Параметр ex=3600 задаёт время жизни ключа Redis (в нашем случае 1 час).

Проверка сессии (авторизация запросов)

Теперь при каждом запросе мы будем проверять, существует ли сессия в Redis.


@app.get("/protected")
async def protected_route(session_id: str):
    # Проверяем, существует ли такая сессия
    username = await redis.get(f"session:{session_id}")
    
    if username is None:
        raise HTTPException(status_code=403, detail="Session expired or invalid")

    return {"message": f"Welcome back, {username}!"}

Здесь происходит следующее:

  • Мы получаем session_id из запроса.
  • Пытаемся найти сессию в Redis.
  • Если сессия отсутствует (или истек TTL), пользователь получает ошибку 403.

Удаление сессии (Logout)

Теперь реализуем удаление сессии. Оно понадобится, если пользователь выйдет из системы.


@app.post("/logout")
async def logout(session_id: str):
    # Удаляем сессию из Redis
    await redis.delete(f"session:{session_id}")
    return {"message": "Logged out successfully"}

Обновление TTL

Для автоматического обновления TTL при каждом новом запросе, можем добавить такую логику:


@app.get("/keep-alive")
async def keep_alive(session_id: str):
    # Проверяем активность сессии
    if not await redis.exists(f"session:{session_id}"):
        raise HTTPException(status_code=403, detail="Session expired")

    # Обновляем TTL
    await redis.expire(f"session:{session_id}", 3600)  # Обновляем TTL до 1 часа
    return {"message": "Session TTL updated"}

Тестирование приложения

Попробуйте запустить:

  1. /login:
    • Отправьте имя пользователя, получите session_id.
  2. /protected:
    • Отправьте session_id.
    • Убедитесь, что доступ есть, если сессия активна.
  3. /logout:
    • Удалите сессию и проверьте, что она больше не работает.

Типичные ошибки и их решение

Если вы получаете ошибку подключения к Redis, проверьте:

  • Работает ли Redis-сервер (redis-server запущен?).
  • Правильный ли порт указан в redis://localhost.
  • Установлены ли библиотеки redis и aioredis.

При отсутствии сессии в /protected убедитесь, что:

  • Время жизни сессии (TTL) не истекло.
  • session_id был передан в запросе корректно.

Эта реализация подходит для небольших и средних приложений. В крупных проектах вы можете использовать Redis-кластеры для большей отказоустойчивости, а также добавить библиотеку для работы с JWT токенами вместо uuid4.

1
Задача
Модуль 4: FastAPI, 9 уровень, 5 лекция
Недоступна
Создание и проверка пользовательской сессии
Создание и проверка пользовательской сессии
1
Задача
Модуль 4: FastAPI, 9 уровень, 5 лекция
Недоступна
Реализация завершения и обновления сессии
Реализация завершения и обновления сессии
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ