Лимит на 200 зрителей поверх MistServer
На VM в Selectel один гигабитный порт. При потоке 3 Мбит/с примерно 300 зрителей забивают его целиком — вместе с запасом для приёма сигнала, SSH и обычной работы сервера.
Перед живым международным турниром я поставил предел в 200 зрителей, около 600 Мбит/с. Моё правило здесь простое: лучше аккуратно отказать 201-му, чем дать перегруженному порту испортить видео всем трёмстам.
В MistServer такого лимита нет
В MistServer OSS 3.x нет штатного глобального ограничения по числу зрителей или суммарной исходящей полосе. Проверил по документации: встречающиеся в сети утверждения об обратном ничем не подтверждены.
Два похожих механизма задачу не решают:
→ LIVE_BANDWIDTH работает как грубый аварийный выключатель. Ответ false убивает весь буфер потока, а не одну новую сессию. Для боевого контура это непригодно
→ maxkbps у RTMP-коннектора ограничивает входящий поток. Зрительскую отдачу он не видит
Значит, решение должно стоять в момент допуска новой сессии.
USER_NEW как точка входа
Триггер USER_NEW отправляет запрос небольшому синхронному обработчику на Python. Он работает как служба systemd и слушает только loopback на :9901.
На каждое подключение обработчик:
→ читает текущее число зрителей из active_streams
→ пропускает ingest-коннекторы мимо лимита — входящий сигнал нельзя случайно отрезать зрительским правилом
→ разрешает новую сессию, пока счётчик ниже 200
→ пишет решение в JSON-журнал: поток, тип коннектора, адрес, счётчик и причина отказа
Так журнал допуска заодно стал аналитикой отказов. Можно увидеть, сколько людей пришло после заполнения порта, не добавляя отдельную систему измерения.
Обработчик сделан fail-open: если служба на :9901 недоступна или не может получить счётчик, MistServer пропускает зрителя. Ошибка моей обвязки не должна погасить работающий эфир. Жёсткую границу полосы позже даст гипервизор.
Первый вариант я зарегистрировал неправильно: новый синхронный обработчик заменил уже работающий асинхронный обработчик телеметрии. Поймал это на проверке конфигурации и добавил ограничитель вторым обработчиком, рядом с прежним. Один триггер умеет обслуживать оба — но только если явно сохранить оба назначения.
Проверка от синтетики до реального отказа
Матрица прошла целиком:
→ обычный зритель допускается
→ при искусственном cap=0 зритель получает отказ
→ ingest проходит даже при cap=0
→ триггер срабатывает на свежем потоке с реальным IP
→ при cap=1 удерживаемая сессия занимает единственное место, а подключение со стороннего облачного IP отклоняется
Последний тест был важнее остальных. Отклонённый зритель не висит на пустом запросе: MistServer возвращает чистый плейлист с #EXT-X-ERROR: Shutting down due to session end. Клиент получает конечный ответ, порт остаётся в рабочем диапазоне.
Старые процессы не подхватывают новый триггер
Есть один эксплуатационный нюанс: процессы потоков, запущенные до регистрации обработчика, продолжают жить со старой конфигурацией. Ограничение начинает действовать для них только после перезапуска потока.
Поэтому порядок выкатки здесь такой: зарегистрировать оба обработчика, проверить их список, перезапустить поток, затем повторить внешний тест. Простое сохранение настройки недостаточно.
Следующий шаг уже помещается в тот же обработчик: MistServer выдаёт каждой сессии ?tkn=, туда ляжет проверка HMAC-подписи ссылки. Жёстким ограничителем станет net0 rate 700 Mbit в Proxmox — после турнира, без переподключения сетевого интерфейса в ночь перед эфиром.
Дальше — по мере поступления.