Системы видеоаналитики обрабатывают терабайты видеоданных в реальном времени — от распознавания номеров до детекции инцидентов на дорогах. В таких условиях любая ошибка управления памятью оборачивается сбоями: пропущенными нарушениями, потерянными доказательствами, остановкой системы на часы. 

Именно поэтому всё больше команд, разрабатывающих высоконагруженные системы видеоаналитики, рассматривают Rust как альтернативу языкам с ручным управлением памятью (C/C++) — как минимум для критических компонентов, где важны предсказуемое время отклика и исключение ошибок работы с памятью.

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

При подготовке статьи комментариями поделился Антон Половихин, руководитель команды разработки сервисов локального комплекса в «Фалькон Тех». Он ответил на вопросы по работе с Rust в высоконагруженных бэкенд-системах и помог уточнить технические детали.

Масштаб задачи: 4000 комплексов, терабайты данных и цена ошибки

Сегодня платформа «Фалькон Тех» используется более чем в 4000 комплексах фото- и видеофиксации. Каждый из них в режиме реального времени обрабатывает видеопоток: детектирует объекты, отслеживает их траектории, распознаёт государственные регистрационные знаки, фиксирует нарушения и передает результаты в информационные системы.

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

При этом комплексы являются частью городской инфраструктуры и должны работать непрерывно, в том числе при пиковых нагрузках и на удаленных объектах с нестабильной связью. Поэтому при разработке бэкенда для нас важна не только производительность, но и предсказуемое поведение системы в течение длительного времени — именно это стало одной из причин выбора Rust.

Почему Rust: безопасность памяти как архитектурное решение

Когда нас спрашивают, почему для бэкенда видеоаналитики был выбран именно Rust, ответ обычно не начинается с производительности или скорости выполнения кода.

В команде объясняют это иначе:

Компилятор Rust берет на себя часть ответственности разработчика. Если проект успешно скомпилировался, то за работу с памятью можно быть гораздо спокойнее.

Антон Половихин, руководитель команды разработки сервисов локального комплекса в «Фалькон Тех»

Именно эта идея стала одной из ключевых при выборе языка.

В высоконагруженных сервисах ошибки работы с памятью — одни из самых неприятных. Они могут долго не проявляться, воспроизводиться только при определенной нагрузке или возникать спустя часы и даже дни после запуска приложения. Поиск таких проблем обычно занимает значительно больше времени, чем их исправление.

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

Это достигается за счет нескольких встроенных механизмов языка:

  • модели владения памятью (ownership);

  • системы заимствований (borrow checker);

  • проверки безопасного доступа к данным из нескольких потоков;

  • строгой системы типов.

В результате целый класс ошибок просто не попадает в продакшен. Речь идет об обращении к уже освобожденной памяти (use-after-free), двойном освобождении памяти (double free), висячих указателях (dangling pointers) и многих ошибках конкурентного доступа к данным. Для системы, которая непрерывно обрабатывает видеопотоки и должна работать без остановок, это дает практический эффект: меньше трудновоспроизводимых сбоев и более предсказуемое поведение сервисов.

При этом Rust не рассматривается как универсальный инструмент. Выбор языка всегда зависит от задачи. 

Если нужно быстро сделать MVP или небольшой сервис, мы скорее выберем Python. Но если речь идет о большой системе, которая должна работать годами, преимущества Rust становятся заметнее.

Антон Половихин, руководитель команды разработки сервисов локального комплекса в «Фалькон Тех»

Еще один показательный момент — применение unsafe. Несмотря на работу с высоконагруженной обработкой видео, мы не используем этот механизм. Возможностей безопасного Rust оказалось достаточно для решения основных задач, поэтому отключать встроенные гарантии языка не потребовалось.

Компилятор Rust гораздо строже относится к коду, чем большинство популярных языков, поэтому иногда приходится потратить больше времени, чтобы реализовать решение в соответствии со всеми требованиями языка. Однако именно эта строгость и была одной из причин выбора Rust.

В Rust гораздо сложнее написать небезопасный код, чем в C++. Ограничения языка здесь не мешают разработчику: наоборот, помогают избежать многих ошибок еще на этапе написания кода.

Антон Половихин, руководитель команды разработки сервисов локального комплекса в «Фалькон Тех»

Архитектура платформы: изоляция компонентов и чистота ядра

В статье «Секрет устойчивых платформ для умного города: SOLID, GRASP и Clean Architecture в деле» мы подробно рассказывали, почему архитектура крупных систем не ограничивается принципами SOLID. Для платформ, которые постоянно развиваются и работают с десятками взаимосвязанных сервисов, не менее важно защитить бизнес-логику от инфраструктурных изменений и сохранить управляемость проекта по мере его роста.

При разработке платформы видеоаналитики мы придерживаемся тех же принципов. В их основе лежат три архитектурных подхода:

  • Clean Architecture — изоляция бизнес-логики от инфраструктуры;

  • Domain-Driven Design (DDD) — проектирование вокруг предметной области, а не используемых технологий;

  • событийное взаимодействие сервисов через RabbitMQ, которое позволяет минимизировать связанность компонентов.

Такой подход оказался особенно важен для платформы, которая постоянно развивается. В систему регулярно добавляются новые модели компьютерного зрения, сервисы обработки данных и интеграции с внешними системами. Архитектура должна позволять развивать каждый из этих компонентов независимо, не затрагивая остальную платформу.

Как отмечает наш старший бэкенд-разработчик:

Такая чистая архитектура очень выгодна, если в будущем придется поменять технологии. Например, сегодня use case получает события через RabbitMQ. Завтра нужно перейти на HTTP-сервер. Мы изменим только адаптер, который передает событие в сценарий. Весь остальной код трогать не будем.

Андрей Борисов, старший бэкенд-разработчик в «Фалькон Тех»

Эта идея хорошо отражает философию разработки платформы. Бизнес-логика не должна зависеть от конкретной базы данных, брокера сообщений или способа взаимодействия между сервисами. Все инфраструктурные детали остаются на внешнем уровне и при необходимости могут изменяться без переработки ядра системы. Именно такую цель мы ставили перед собой при проектировании архитектуры.

В этом смысле архитектурные принципы и Rust решают разные задачи.

Rust отвечает за надежность внутри отдельного сервиса: помогает избежать ошибок управления памятью, делает безопаснее многопоточную обработку данных и снижает вероятность трудновоспроизводимых сбоев.

Архитектурные подходы отвечают за устойчивость всей платформы:

  • позволяют независимо развивать отдельные сервисы;

  • упрощают замену инфраструктурных компонентов;

  • делают систему более удобной для тестирования и сопровождения;

  • помогают масштабировать платформу без накопления технического долга.

Именно сочетание этих двух уровней — надежного языка и продуманной архитектуры — позволяет развивать платформу на протяжении многих лет, не превращая ее в сложный для сопровождения монолит.

Как система выдерживает нагрузку и сбои на практике

В статье «На каком стеке и железе работает умное наблюдение в вашем городе: обзор технологий от разработчиков видеоаналитики» мы рассказывали, как устроена технологическая платформа «Фалькон Тех». 

Кратко напомним: события от программно-аппаратных комплексов поступают в очередь сообщений, после чего распределяются между микросервисами обработки, работающими в Kubernetes. Такая архитектура позволяет независимо масштабировать отдельные сервисы, не блокировать обработку при всплесках нагрузки и сохранять работоспособность системы при временных отказах отдельных компонентов. 

Теперь посмотрим, как это реализовано на практике.

Устойчивость платформы строится на нескольких принципах:

  • очереди сообщений сглаживают пики нагрузки;

  • Kubernetes автоматически масштабирует сервисы обработки;

  • локальное буферизование позволяет комплексам работать без постоянной связи с облаком;

  • Prometheus и Grafana обеспечивают непрерывный мониторинг состояния платформы.

Все события от комплексов сначала попадают в RabbitMQ, а затем постепенно разбираются сервисами обработки. Если поток данных увеличивается, Kubernetes автоматически запускает дополнительные экземпляры сервисов. При временной потере связи комплексы продолжают работать локально, сохраняя данные в собственной очереди и передавая их после восстановления соединения.

Этот принцип хорошо описывает наш сотрудник:

Основная идея заключается в том, что данные и фотофиксации с комплексов поступают в очередь сообщений в облаке. Затем их постепенно обрабатывают сервисы облачной инфраструктуры. В Kubernetes эти сервисы автоматически масштабируются: если нагрузка растет, запускается больше экземпляров, чтобы быстрее обработать очередь. Когда нагрузка снижается, количество экземпляров также уменьшается.

Антон Половихин, руководитель команды разработки сервисов локального комплекса в «Фалькон Тех»

Даже если один экземпляр сервиса выходит из строя, обработка не останавливается. Сообщения продолжают накапливаться в очереди, Kubernetes запускает новый экземпляр, после чего тот автоматически возвращается к обработке очереди.

Rust в этой архитектуре отвечает за надежность самих сервисов обработки. Очереди сообщений, Kubernetes и мониторинг обеспечивают устойчивость распределенной системы, а Rust снижает вероятность внутренних ошибок благодаря безопасному управлению памятью и отсутствию пауз сборщика мусора. 

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

Выводы: что дает Rust в высоконагруженной видеоаналитике

Опыт разработки платформы «Фалькон Тех» показал, что Rust — это не универсальное решение для любых задач, а инструмент, который подходит для высоконагруженных серверных сервисов, где важны предсказуемая производительность, надежность и эффективное использование ресурсов.

За время разработки мы получили несколько практических преимуществ:

  • стабильную производительность без пауз сборщика мусора;

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

  • эффективное использование памяти, что особенно важно при обработке большого количества параллельных задач;

  • уверенное масштабирование сервисов при росте нагрузки;

  • меньше критических ошибок в эксплуатации по сравнению с традиционными системными языками.

При этом язык не стал единственным фактором успеха проекта. Надежность платформы обеспечивается сочетанием нескольких инженерных решений: микросервисной архитектуры, очередей сообщений, автоматического масштабирования в Kubernetes, постоянного мониторинга и возможностей Rust как языка системного программирования.

Мы выбрали Rust не потому, что это «модный» язык, а потому, что он хорошо решает конкретные инженерные задачи, с которыми ежедневно сталкивается платформа видеоаналитики: помогает создавать производительные сервисы, снижает вероятность ошибок и позволяет увереннее масштабировать систему по мере роста нагрузки.

Для нас Rust стал одним из инструментов построения надежной платформы, которая должна стабильно работать круглосуточно и без выходных — независимо от количества подключенных комплексов и объема поступающих данных.