Назад к портфолио

Новое Радио - стабильный backend и realtime-эфир

Новое Радио: новый backend и стабильный эфир без переписывания сайта

Задача

Заказчик пришёл уже с живым сайтом. Для слушателя продукт выглядел рабочим: плеер, разделы, контент. Для эксплуатации картина была другой — сайт постоянно находился в зоне операционного риска.

Наше решение

Заказчик пришёл уже с живым сайтом. Для слушателя продукт выглядел рабочим: плеер, разделы, контент. Для эксплуатации картина была другой — сайт постоянно находился в зоне операционного риска.

На входе:

  • публичный сайт novoeradio.by;
  • код в одном процессе: React SPA + Node.js + PostgreSQL + канал обновлений эфира в реальном времени;
  • на проде — массовые рестарты процесса приложения (порядка 1200+ за 15 часов) при относительно низкой нагрузке;
  • устаревший runtime и признаки нестабильности в коде/конфигурации — а не классическое «сайт не тянет трафик».

Отдельно важно: рядом существовал второй сайт narodnoeradio.by — по сути копия 1в1 с другим брендом. Это сразу ставило требование не плодить два независимых backend’а, а заложить white-label модель.

Нужно было не «подкрутить Node и надеяться», а:

  1. честно разобрать текущее состояние;
  2. понять, можно ли сохранить фронт;
  3. спроектировать целевой backend под реальные роли нагрузки;
  4. зафиксировать scope 1в1 и путь миграции без даунтайма.

novoe-radio-ill-01-monolith

Этап диагностики: от ревью до ТЗ

Работу начали не с «сразу пишем новый сервис», а с короткой инженерной цепочки. Это позволило согласовать решение до старта разработки и не расползтись по scope.

1. Короткое код-ревью

Неглубокий, но достаточный разбор подтвердил главное:

  • один процесс плохо подходит под смешанную нагрузку «контент API + realtime»;
  • runtime устарел;
  • в коде и конфигурации есть признаки аварийности;
  • рестарты — следствие архитектуры, а не пикового трафика.

Вывод: обновление Node само по себе проблему не закроет. Нужен новый backend с разделением ответственности.

2. Ревью миграции: можно ли оставить фронт

Ключевой вопрос заказчика: придётся ли переписывать сайт целиком.

Ответ: фронт переиспользуем (сложность средняя), если аккуратно сохранить внешние контракты API и канала обновлений эфира.
Целевой стек:

  • Go — realtime и горячие чтения;
  • Symfony / FrankenPHP — CMS, САП, контентное API.

Первый логичный вынос из монолита: получение метаданных эфира и рассылка обновлений слушателям.
Миграция — поэтапная: сначала критичный realtime-контур, затем контентный backend, затем вывод старого стека из эксплуатации.

3. Технический аудит

Полноценный аудит зафиксировал вердикт: backend в предаварийном состоянии.

На уровне категорий находок:

  • критичные — вопросы безопасности и доступа, которые нельзя оставлять в текущем виде;
  • высокие — архитектурный монолит, нестабильный realtime, слабая устойчивость к ошибкам;
  • средние и ниже — технический долг, отсутствие тестов, слабая эксплуатационная документация и неявные контракты между частями системы.

Отдельно зафиксировали план временной стабилизации на период до запуска нового backend — чтобы не «чинить продакшен бесконечными патчами» вместо целевой замены.

4. Углублённое ревью и продуктовая рамка ТЗ

На этом шаге сдвинулись с чистой техники на структуру продукта. В ТЗ нужно было отразить не только сервер, но и то, что сайт реально отдаёт слушателю и редакции:

  • публичные разделы: музыка, акции, подкасты, новости, программы, ведущие и др.;
  • панель редакции;
  • стриминг и Now Playing;
  • контентные сущности;
  • визуальную систему бренда;
  • принцип white-label: один код, разные brand-config.

Именно здесь зафиксировали, что второй сайт — не «отдельный проект», а второй бренд на общей платформе.

5. Черновик ТЗ на новый backend

Собрали ТЗ под согласованные решения:

  • цель — новый backend, фронт на 1-м этапе сохраняем;
  • принцип 1в1 — без расширения продуктового scope;
  • архитектура Go + Symfony/FrankenPHP;
  • разделы сайта, панель редакции, стриминг/плееры, контракты интеграций;
  • мультибренд Novoe + Narodnoe;
  • миграция без даунтайма — поэтапно, с возможностью безопасного отката;
  • явный блок вопросов, которые нужно подтвердить у заказчика до финальной приёмки.

Сквозной тезис всей цепочки: ломается не от нагрузки — от архитектуры и дисциплины эксплуатации.

Главный риск миграции — не «какой язык выбрать», а сохранение контрактов между фронтом и backend.

novoe-radio-ill-02-process

Что реализовано

В итоге сайт переведён на связку с раздельными ролями под нагрузку и удобство сопровождения.

Общая схема

Упрощённо система состоит из четырёх слоёв:

  • React — то, что видит слушатель: плеер, разделы, формы, интерактив;
  • PHP / Symfony — данные сайта по API и панель редакции;
  • Go (realtime) — слежение за эфиром, текущий трек, мгновенная рассылка во все открытые вкладки;
  • PostgreSQL — новости, программы, треки, настройки, история эфира.

В production страницы, API, медиа и канал обновлений эфира доступны с одного адреса сайта — через единую точку входа.

Рабочий путь — Symfony + Go + React. Старый backend использовался как эталон поведения на время миграции и затем выведен из боевого контура.

novoe-radio-ill-03-architecture

Роли технологий

React — лицо сайта

  • главная и разделы: новости, программы, подкасты, чарты, акции, о станции и др.;
  • аудиоплеер и отображение текущего трека / обложки;
  • работа с API: списки, лайки, голосования, подписки;
  • получение обновлений эфира в реальном времени.

В production интерфейс собирается и отдаётся вместе с основным приложением. В разработке фронт можно поднимать отдельно для удобства правок.

PHP / Symfony — контент и редакция

  • публичное API сайта: настройки, баннеры, новости, программы, чарты, треки, акции, квизы и связанные сущности;
  • панель редакции: создание и правка материалов, загрузка обложек и аудио;
  • расписание программ и логика «текущая программа»;
  • служебные фоновые задачи по данным сайта;
  • уведомление realtime-сервиса о изменениях настроек эфира без полного рестарта платформы.

PHP — основной источник правды по материалам сайта и инструмент редакции. Это замена прежней схемы управления контентом на Node.

Go — эфир «здесь и сейчас» Go выбран для задач, где важны постоянная фоновая работа и много одновременных подключений:

  1. получение метаданных текущего эфира из аудиопотоков;
  2. сопоставление строки эфира с каталогом треков;
  3. мгновенная доставка обновлений всем открытым вкладкам;
  4. сопутствующие действия эфира: история, плейлист, синхронизация с внешними витринами треков;
  5. ускорение части частых чтений, чтобы разгрузить контентный backend при высокой посещаемости.

База и медиа

  • текстовый и структурный контент — в PostgreSQL;
  • обложки и аудио — в отдельном файловом хранилище, чтобы обновление приложения не затирало медиа;
  • «что играет сейчас» держится в realtime-контуре и рассылается клиентам; история эфира сохраняется в базе.

Как работает Now Playing

  1. В системе задаются аудиопотоки и их параметры. Потоки без стабильных метаданных можно не опрашивать.
  2. Realtime-сервис загружает актуальный список активных потоков.
  3. Периодически читает метаданные эфира и нормализует строку вида «Исполнитель — Трек».
  4. Ищет совпадение в каталоге треков и подставляет обложку / связанные данные.
  5. Если трек изменился — рассылает обновление всем подключённым клиентам.
  6. При открытии сайта плеер сразу получает текущее состояние, не дожидаясь следующей смены трека.
  7. При изменениях в панели редакции realtime-контур обновляет конфигурацию без рестарта всей платформы.

Именно поэтому realtime вынесен отдельно: постоянное сопровождение эфира и рассылка обновлений — нагрузка другого типа, чем «открыть новость» или «сохранить баннер». novoe-radio-ill-04-now-playing

Окружения и эксплуатация

Проект поддерживает два основных контура.

Development — для команды: backend, база, realtime, удобная разработка фронта и безопасная проверка писем/подписок без отправки «в бой». Production — основное приложение, realtime-сервис, база, постоянное хранение медиа, HTTPS и маршрутизация запросов к нужному контуру.

Окружения воспроизводимы через Docker: одинаковая логика запуска у разработчиков и на сервере, при этом секреты и параметры среды не хранятся в коде.

Что получили слушатель и редакция

Публичный сайт

  • оформление: фон, логотип, баннеры, слайды, рекламные блоки;
  • список аудио- и видеопотоков;
  • новости, акции, программы и архив, подкасты, ведущие;
  • чарты, новинки, голосования и оценки треков;
  • квизы и подписка на программы;
  • плеер с живыми метаданными эфира.

Панель редакции Редакция работает в веб-интерфейсе на Symfony: создание и правка материалов, загрузка файлов, управление сущностями сайта — без прежней хрупкой схемы на Node.

Мультибренд Один код и общая логика; разные brand-config для Novoe и Narodnoe — цвета, оформление, бренд-параметры — без дублирования backend-логики.

Ключевые решения проекта

  • не переписывать фронт на первом этапе — мигрировать backend за внешними контрактами;
  • не «лечить» монолит обновлением runtime — заменить архитектуру;
  • вынести realtime и горячие чтения в Go;
  • оставить CMS, контент и редакцию на Symfony;
  • использовать старый backend только как эталон поведения на время переноса;
  • заложить white-label под Novoe + Narodnoe;
  • держать продуктовый scope 1в1, чтобы миграция была контролируемой;
  • делать cutover поэтапно, с возможностью безопасного отката, а не «большим взрывом».

Результат

Заказчик получил не косметический фикс нестабильного Node-приложения, а новую эксплуатационную модель сайта радиостанции:

  • контент и редакция — в управляемом Symfony-контуре;
  • эфир «здесь и сейчас» — в отдельном Go-сервисе;
  • интерфейс слушателя — на уже знакомом React без полного redesign;
  • инфраструктура — в Docker с понятным разделением dev/prod и безопасным хранением медиа;
  • второй бренд — как конфигурация платформы, а не как второй независимый сайт «с нуля».

efore after

Результат

Заказчик получил не косметический фикс нестабильного Node-приложения, а новую эксплуатационную модель сайта радиостанции. Это типичный сценарий для digital-продукта, который «вроде работает», но уже находится в зоне постоянного операционного риска. Сначала — честный аудит и контракты. Затем — целевая архитектура под реальные роли нагрузки.