У цій лекції ми нарешті дійдемо до того, що роблять майже всі сучасні великі веб-застосунки — збережемо сесії користувачів у Redis. Як ти й здогадався, Redis тут виступає як швидкий і зручний інструмент для зберігання тимчасових даних.
Користувацька сесія — це як квиток у кіно: вона підтверджує, що користувач (або система) вже "законно зайшов" у наш застосунок. Сесія може містити ідентифікатор користувача, токен авторизації, часову мітку та інші дані. Навіщо їх зберігати? Щоб користувач міг продовжити роботу в застосунку навіть після закриття браузера або перезавантаження сторінки.
Проте якщо зберігати сесії у базі даних (наприклад, PostgreSQL), це може швидко стати вузьким місцем, адже користувачі роблять десятки, якщо не сотні запитів до сесій щосекунди. Redis же, будучи швидкою in-memory базою даних, ідеально підходить для цього завдання.
Переваги кешування сесій у Redis:
- Миттєва швидкість: Redis зберігає дані в оперативній пам'яті, що робить доступ до сесій неймовірно швидким.
- Стійкість: Redis підтримує реплікацію та дампування даних, мінімізуючи ризик втрати сесій.
- Гнучкість: ти можеш налаштовувати TTL для сесій, щоб вони автоматично видалялися через певний час.
- Оновлення даних "на льоту": хочеш відстежувати активність користувачів? Легко можеш оновлювати дані сесій без зайвих затримок.
Розберемо сесії через Redis як просту послідовність дій:
- Ініціалізація сесії: коли користувач логіниться, створюється нова сесія, і її ідентифікатор зберігається в Redis разом з даними (наприклад, ID користувача).
- Читання сесії: при наступних запитах ідентифікатор сесії використовується для вилучення даних з Redis.
- Оновлення TTL: TTL (час життя) можна оновлювати кожного разу, коли користувач виконує запит, щоб його сесія "не скінчилась" під час роботи.
- Видалення сесії: як тільки користувач вийшов, сесія видаляється.
Встановлення й налаштування оточення
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 сесії оновлено"}
Тестування застосунку
Спробуй запустити:
- /login:
- Надішли ім'я користувача, отримай
session_id.
- Надішли ім'я користувача, отримай
- /protected:
- Надішли
session_id. - Переконайся, що доступ є, якщо сесія активна.
- Надішли
- /logout:
- Видали сесію і перевір, що вона більше не працює.
Типові помилки та їх вирішення
Якщо ти отримуєш помилку підключення до Redis, перевір:
- Чи працює Redis-сервер (
redis-serverзапущений?). - Чи правильно вказаний порт у
redis://localhost. - Чи встановлені бібліотеки
redisіaioredis.
Якщо сесія відсутня в /protected, переконайся, що:
- Час життя сесії (TTL) не вичерпано.
session_idбув переданий у запиті коректно.
Ця реалізація підходить для невеликих та середніх застосунків. У великих проєктах ти можеш використовувати Redis-кластери для більшої відмовостійкості, а також додати бібліотеку для роботи з JWT токенами замість uuid4.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ