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="Невірний користувач")

    # Генерація унікального ідентифікатора сесії
    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="Сесія протермінована або недійсна")

    return {"message": f"Радий бачити тебе знову, {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": "Вихід виконано успішно"}

Оновлення 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="Сесія протермінована")

    # Оновлюємо TTL
    await redis.expire(f"session:{session_id}", 3600)  # Оновлюємо TTL до 1 години
    return {"message": "TTL сесії оновлено"}

Тестування застосунку

Спробуй запустити:

  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.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ