Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

19 Commits

Folders and files

Repository files navigation

Redis Pub/Sub Simulator на Python

Учебный проект демонстрирует работу Redis Pub/Sub через простой веб-интерфейс: пользователь публикует сообщения в Redis-каналы, включает и выключает подписчиков и сразу видит, кто получил сообщение, а для кого оно было потеряно.

Главная идея проекта - показать, что Redis Pub/Sub работает как система мгновенной доставки событий, а не как очередь. Если подписчик был offline во время публикации, Redis не сохранит сообщение для него и не доставит его позже.

1. Что демонстрирует проект

Проект помогает наглядно разобрать базовые принципы Redis Pub/Sub:

  • сообщение публикуется в конкретный канал через PUBLISH;
  • обычные подписчики получают сообщения через SUBSCRIBE;
  • pattern-подписчик получает сообщения через PSUBSCRIBE;
  • сообщение получают только те подписчики, которые сейчас online и подписаны на подходящий канал;
  • offline-подписчики не получают старые сообщения после возвращения online;
  • frontend обновляется без перезагрузки страницы через WebSocket.

Интерфейс специально сделан как симулятор: сверху показаны каналы Redis, ниже подписчики, затем форма публикации, кнопки переключения online/offline и лог событий.

2. Что видно в интерфейсе

На странице есть три основных блока.

Каналы

В блоке каналов отображаются:

  • имя канала;
  • количество активных подписчиков на этот канал;
  • последние опубликованные сообщения.

Каналы в проекте:

  • chat:room42 - общий чат;
  • notifications:user1 - личные уведомления пользователя;
  • cache:invalidate - события очистки кеша.

Подписчики

Каждый подписчик отображается отдельной карточкой. В карточке видно:

  • имя подписчика;
  • статус online или offline;
  • список обычных или pattern-подписок;
  • последнее полученное сообщение;
  • общее количество полученных сообщений.

Лог событий

Лог показывает, что происходило в системе:

[16:53:25] PUBLISH chat:room42 "Привет!" -> 4 получателя
[16:54:35] PUBLISH cache:invalidate "Событие B" -> 1 получатель
[16:55:10] Sub D: UNSUBSCRIBE offline

Если сообщение не получил никто, в логе видно, что оно потеряно:

PUBLISH cache:invalidate "clear" -> 0 получателей, сообщение потеряно

3. Как устроен поток данных

Когда пользователь нажимает PUBLISH, происходит такой путь:

  1. Браузер отправляет POST /api/publish на FastAPI backend.
  2. Backend проверяет канал и текст сообщения.
  3. Backend формирует JSON payload с id, channel, text, created_at и publisher.
  4. Backend публикует payload в Redis через команду PUBLISH.
  5. Активные подписчики, у которых есть SUBSCRIBE или PSUBSCRIBE, получают событие из Redis.
  6. Backend обновляет состояние подписчиков.
  7. Backend отправляет новое состояние и лог в браузер через WebSocket /ws.
  8. Интерфейс перерисовывает каналы, подписчиков и лог.

Важно: backend не хранит очередь сообщений. В проекте есть только демонстрационное состояние интерфейса: счетчики, последние сообщения и лог.

4. Чем Pub/Sub отличается от очереди

Redis Pub/Sub не хранит сообщения.

Если подписчик был offline в момент публикации, сообщение для него потеряно. После возврата online Redis не доставит старые публикации.

Это ключевое отличие от очередей. В очередях или Redis Streams сообщение может храниться до обработки. В Pub/Sub сообщение существует только в момент публикации и доставляется только текущим активным подписчикам.

Для гарантированной доставки лучше использовать:

  • Redis Streams;
  • очереди задач;
  • RabbitMQ;
  • Kafka;
  • другие брокеры сообщений с подтверждением доставки.

В этом проекте они специально не используются, чтобы не смешивать Pub/Sub с очередями.

5. Архитектура проекта

redis_pub_sub/
├── app/
│   ├── main.py              # FastAPI-приложение, главная страница и WebSocket
│   ├── config.py            # настройки приложения
│   ├── redis_client.py      # создание Redis-клиента
│   ├── schemas.py           # Pydantic-схемы API
│   ├── api/
│   │   ├── publish.py       # endpoint публикации сообщений
│   │   └── subscribers.py   # состояние, toggle, clear
│   ├── pubsub/
│   │   ├── manager.py       # тонкий фасад для API и main.py
│   │   ├── service.py       # координация Pub/Sub-компонентов
│   │   ├── publisher.py     # Redis PUBLISH и формирование payload
│   │   ├── lifecycle.py     # startup/shutdown/toggle подписчиков
│   │   ├── events.py        # обработка доставленных Redis-сообщений
│   │   ├── logging.py       # события лога и подписи количества получателей
│   │   ├── subscriber.py    # отдельный Redis Pub/Sub подписчик
│   │   └── patterns.py      # проверка обычных и pattern-подписок
│   ├── simulator/
│   │   ├── state.py         # состояние интерфейса, счетчики, лог, аналитика каналов
│   │   └── websocket.py     # подключение и broadcast WebSocket-клиентов
│   └── static/
│       └── index.html       # HTML/CSS/JavaScript интерфейс
├── docker-compose.yml
├── Dockerfile
├── requirements.txt
├── Makefile
└── README.md

6. Какие подписчики есть в проекте

Подписчик Стартовый статус Подписки Что получает
Sub A online chat:room42, notifications:user1 сообщения чата и уведомления
Sub B online chat:room42 только сообщения чата
Sub C online chat:* все каналы, подходящие под pattern chat:*
Sub D offline chat:room42, cache:invalidate чат и cache-события, но только когда online

Sub C использует pattern-подписку. Он получает сообщения из chat:room42, потому что канал подходит под шаблон chat:*, но не получает сообщения из notifications:user1 и cache:invalidate.

7. Как запустить проект

Через Docker:

docker compose up --build

В текущем docker-compose.yml web-приложение проброшено на порт 8010, потому что на машине уже были заняты 8000 и 8001.

Откройте:

http://localhost:8010

Локально без Docker:

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
redis-server
uvicorn app.main:app --reload

При локальном запуске откройте:

http://localhost:8000

Если Redis запущен по другому адресу, задайте переменную окружения:

REDIS_URL=redis://localhost:6379/0 uvicorn app.main:app --reload

Redis-клиенты создаются через общий async connection pool. Размер пула и health-check можно менять переменными:

REDIS_MAX_CONNECTIONS=20
REDIS_HEALTH_CHECK_INTERVAL=30

8. Как проверить основные сценарии

Сценарий 1. Обычная доставка в чат

  1. Выберите канал chat:room42.
  2. Введите сообщение Привет!.
  3. Нажмите PUBLISH.

Ожидаемый результат:

  • Sub A получит сообщение;
  • Sub B получит сообщение;
  • Sub C получит сообщение через pattern chat:*;
  • Sub D получит сообщение только если он online.

Сценарий 2. Уведомление одному пользователю

  1. Выберите канал notifications:user1.
  2. Отправьте сообщение У вас новое уведомление.

Ожидаемый результат:

  • сообщение получит только Sub A;
  • Sub B, Sub C и Sub D его не получат.

Сценарий 3. Cache invalidation

  1. Включите Sub D, если он offline.
  2. Выберите канал cache:invalidate.
  3. Отправьте сообщение clear user:42.

Ожидаемый результат:

  • сообщение получит Sub D;
  • остальные подписчики его не получат.

Сценарий 4. Потеря сообщения

  1. Выключите Sub D.
  2. Выберите канал cache:invalidate.
  3. Отправьте сообщение clear user:42.
  4. Включите Sub D обратно.

Ожидаемый результат:

  • в момент публикации будет 0 получателей;
  • в логе будет видно, что сообщение потеряно;
  • после возвращения online Sub D не получит старое сообщение.

Так проявляется главное свойство Redis Pub/Sub: это не очередь.

9. API

Метод Endpoint Назначение
GET / HTML-интерфейс
GET /api/state состояние каналов, подписчиков и лога
POST /api/publish публикация одного сообщения
POST /api/publish/batch пакетная публикация 5 сообщений
POST /api/subscribers/{subscriber_id}/toggle переключение подписчика online/offline
POST /api/clear сброс счетчиков, последних сообщений, визуального списка сообщений и лога
WS /ws WebSocket-события для обновления интерфейса

Пример публикации:

curl -X POST http://localhost:8010/api/publish \
  -H "Content-Type: application/json" \
  -d '{"channel":"chat:room42","message":"Привет!"}'

Пример ответа:

{
  "status": "published",
  "channel": "chat:room42",
  "message": "Привет!",
  "delivered_to": 3
}

10. Какие команды Redis используются

В проекте используются основные команды Redis Pub/Sub:

  • PUBLISH channel message - публикация сообщения в канал;
  • SUBSCRIBE channel - подписка на конкретный канал;
  • PSUBSCRIBE pattern - подписка на каналы по шаблону;
  • UNSUBSCRIBE channel - отмена обычной подписки;
  • PUNSUBSCRIBE pattern - отмена pattern-подписки.

11. Что намеренно не используется

В проекте нет:

  • Redis Streams;
  • Celery;
  • RabbitMQ;
  • Kafka;
  • базы данных;
  • авторизации;
  • регистрации пользователей;
  • очередей задач;
  • хранения истории сообщений для offline-подписчиков.

Это сделано специально: цель проекта - показать именно Redis Pub/Sub и его ограничение по доставке offline-подписчикам.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages