Платформа комментариев в реальном времени

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

Целевая аудитория этой статьи делится на следующие роли:

  • Технические работники
  • Студенты
  • Инженерные менеджеры

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

Отказ от ответственности. Вопросы по дизайну системы носят субъективный характер. Эта статья написана на основе исследования, которое я провел по этой теме, и может отличаться от реальных реализаций. Не стесняйтесь делиться своими отзывами и задавать вопросы в комментариях. Некоторые из связанных ресурсов являются аффилированными.

Что такое живое видео?

Живое видео — это потоковое видео через Интернет в режиме реального времени без предварительной записи и хранения. Телевизионные трансляции, стримы видеоигр и видеоролики в социальных сетях часто транслируются в прямом эфире [27].

Живое видео отличается от обычного видео, потому что живое видео приводит к резкому трафику. Обычно живые видео более привлекательны и их смотрят в три раза больше, чем обычные видео. Кроме того, социальные сети, такие как Facebook, показывают прямые трансляции вверху ленты новостей, тем самым увеличивая вероятность того, что пользователи (клиенты) просматривают прямые трансляции [16].

Что такое живые комментарии?

Работа в режиме реального времени позволяет клиентам воспринимать платформу как место деятельности [3]. Комментирование в реальном времени — это функция, которая позволяет клиентам публиковать комментарии к живым видео в режиме реального времени. Комментарии в прямом эфире обычно представляют собой смесь отзывов клиентов о живом видео или случайных разговоров между клиентами. Кроме того, живые комментарии позволяют стримеру живого видео взаимодействовать с клиентами во время прямой видеотрансляции.

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

Если вы планируете подписаться на Medium, используя мою реферальную ссылку, я хотел бы сообщить вам, что я получу часть членских взносов в качестве вознаграждения за то, что пригласил вас. Это помогает мне продолжать создавать ценный контент. Однако я хочу заверить вас, что это никак не повлияет на стоимость вашей подписки. Вы по-прежнему получите те же преимущества и возможности, что и любой другой участник Medium. Спасибо, что рассмотрели мою реферальную ссылку и поддержали мою работу!

Вопросы, которые нужно задать интервьюеру

Кандидат

  1. Каковы основные варианты использования системы?
  2. Служба комментариев в реальном времени поддерживает только текст?
  3. Распределены ли клиенты по всему миру?
  4. Каково количество активных пользователей в день (DAU)?
  5. Каково общее количество ежедневных живых видео в системе?
  6. Каково среднее количество живых комментариев к живому видео?
  7. Каково ожидаемое соотношение чтения и записи живых комментариев?
  8. Каково максимальное количество одновременных пользователей, которые смотрят одно и то же живое видео?
  9. Следует ли архивировать комментарии к потоковым видео для экономии места?

Интервьюер

  1. Клиенты могут взаимодействовать друг с другом в режиме реального времени с помощью комментариев в прямом эфире на Facebook.
  2. Да
  3. Да
  4. 100 миллионов активных пользователей в день
  5. 200 миллионов ежедневных прямых трансляций
  6. 10
  7. 100: 1
  8. 80 миллионов
  9. Да, нет необходимости поддерживать повтор живых комментариев.

Требования

Функциональные требования

  • Издатели (клиенты) могут оставлять комментарии в режиме реального времени к прямому видео на Facebook.
  • Получатели (клиенты), просматривающие живое видео на Facebook, должны иметь возможность просматривать живые комментарии в режиме реального времени.
  • Получатель должен иметь возможность просматривать в режиме реального времени активную ленту живых комментариев к каждому живому видео, прокручивая ленту новостей Facebook.
  • Должны поддерживаться индикаторы ввода (функция кто-то печатает).
  • Общее количество комментариев должно быть видно на каждом живом видео.
  • Служба комментариев в реальном времени должна работать с клиентами по всему миру.
  • Служба комментариев в реальном времени должна обслуживать миллионы одновременных клиентов.

Нефункциональные требования

  • Высокая доступность
  • Отказоустойчивой
  • Низкая задержка
  • Масштабируемость
  • Конечная согласованность

Дизайн базы данных системы живых комментариев

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

Проект схемы базы данных

Основными объектами реляционной базы данных являются таблица комментариев, таблица видео и таблица пользователей. Между пользователями и таблицами комментариев существует связь 1-to-many. Отношения между таблицами видео и комментариев — 1 ко многим. Отношения между таблицами пользователей и видео составляют один ко многим.

Тип хранилища данных

Содержимое живых комментариев содержит только текстовые данные и не содержит медиафайлов. Очень быстрая и надежная база данных, которая не только постоянно хранит данные, но и обеспечивает быстрый доступ к данным, является ключевой функцией для создания службы комментариев в реальном времени. Постоянное хранение живых комментариев необходимо для извлечения комментариев в более поздний момент времени. Кроме того, данные, хранящиеся в постоянном хранилище, можно использовать для целей аудита [1]. Реляционная база данных предлагает четко определенные структуры для комментариев, пользователей и видео. Реляционная база данных будет оптимальным выбором, когда набор данных небольшой. Однако реляционная база данных будет неоптимальным решением для службы комментариев в реальном времени из-за следующих ограничений масштабируемости [14]:

  • внутренняя структура данных добавляет задержку к операциям с данными
  • сложные запросы необходимы для реинтеграции данных из-за разделения данных

Создание индексов базы данных для столбцов video_id и created_at повысит производительность операций чтения за счет медленных операций записи. База данных NoSQL, такая как Apache Cassandra, может использоваться в качестве постоянного хранилища данных для живых комментариев по следующим причинам [14]:

  • Механизм хранения на основе дерева слияния с логической структурой (LSM) обеспечивает чрезвычайно высокую производительность при записи.
  • Бессхемная модель данных снижает накладные расходы на объединение разных таблиц.
  • изначально оптимизирован для данных временных рядов

Apache Cassandra не оптимизирован для операций чтения из-за особенностей механизма хранения на основе LSM. Базу данных в памяти, такую ​​как Redis, можно использовать в сочетании с Apache Cassandra, чтобы повысить производительность чтения и сделать уровень хранения данных масштабируемым и производительным для живых комментариев. Кэш Redis с поддержкой георепликации со временем жизни (TTL) 1 секунда может быть добавлен в качестве уровня кэша поверх Apache Cassandra для повышения производительности чтения. Кроме того, живые комментарии, опубликованные в чрезвычайно популярном живом видео, могут храниться в кеше на границах сети, чтобы уменьшить задержку [1].

Тип данных наборов в Redis можно использовать для эффективного хранения живых комментариев. Собственная логика дедупликации типа данных наборов гарантирует, что живые комментарии хранятся в памяти без дополнительной логики для предотвращения повторения живых комментариев. Тип данных отсортированного набора в Redis можно использовать для обеспечения целостности данных, поддерживая обратный хронологический порядок живых комментариев. Тип данных отсортированного набора может использовать метку времени в активных комментариях для сортировки активных комментариев без реализации пользовательского алгоритма сортировки. Метаданные издателя живого комментария могут храниться в хэш-типе данных Redis для быстрого поиска [2], [13], [14].

Получатели, которые географически расположены ближе к издателю комментария в реальном времени, увидят комментарий в реальном времени мгновенно, в то время как получатели, находящиеся на другом континенте, могут увидеть комментарий в реальном времени с небольшой задержкой (менее 250 мс), чтобы обеспечить доступность и разделение. толерантность в теореме CAP [1].

Дополнительные учебные ресурсы по проектированию систем

Вы готовитесь к собеседованию по системному дизайну и чувствуете себя ошеломленным сложностью процесса? Вы хотите получить знания и уверенность, чтобы успешно пройти собеседование и продвинуться по карьерной лестнице в области разработки программного обеспечения? Тогда вам обязательно нужно пройти курс Собеседование по системному дизайну от DesignGurus!

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

Не упустите эту возможность поднять свои навыки проектирования систем на новый уровень и достичь своих карьерных целей. Запишитесь на курс Собеседование по проектированию систем от DesignGurus сегодня и сделайте первый шаг к успеху!

Живое комментирование Высокоуровневый дизайн

Пишите глобально и читайте локально

Базу данных можно настроить для асинхронной репликации данных между серверами баз данных в центрах обработки данных, расположенных на разных континентах. С точки зрения непрофессионала, данные всегда извлекаются сервером с серверов баз данных в локальном центре обработки данных, в то время как обновления данных асинхронно записываются на серверы баз данных в центрах обработки данных по всему миру. Этот метод известен как глобальная запись и локальное чтение. Например, когда клиент, находящийся в США, публикует прямой комментарий к живому видео на Facebook, клиенты, находящиеся в Европе, не увидят этот комментарий мгновенно из-за асинхронного характера репликации данных. Ниже приведены недостатки подхода записи в глобальном масштабе и локального чтения для реализации службы комментариев в реальном времени [12]:

  • значительное использование полосы пропускания при репликации данных
  • живые комментарии не будут в реальном времени из-за асинхронной репликации
  • плохая общая задержка

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

Пишите локально и читайте глобально

Данные всегда записываются на сервер базы данных в локальном центре обработки данных, в то время как данные извлекаются путем запроса серверов в центрах обработки данных по всему миру, как показано на рис. 8. Этот метод известен как модель на основе извлечения локальная запись и глобальное чтение. Временная метка на живых комментариях может использоваться для проверки наличия более новых комментариев, опубликованных с момента последнего выполнения запроса. Сервер в локальном центре обработки данных объединит все опубликованные живые комментарии и вернет ответ клиенту. Использование полосы пропускания относительно ниже, поскольку данные не реплицируются глобально. Однако модель на основе извлечения приведет к уменьшению задержки, поскольку сервер должен опрашивать все центры обработки данных по всему миру при каждой операции чтения [12]. Таким образом, не используйте основанную на вытягивании модель локального написания и глобального чтениядля реализации живых комментариев.

Данные всегда записываются на сервер распределенной базы данных в локальном центре обработки данных. На рис. 9, когда клиент публикует живой комментарий к живому видео в Facebook, операция записи транслируется сервером в несколько центров обработки данных по всему миру. Этот метод известен как модель на основе push-уведомлений, локальная запись и глобальное чтение. Основанная на push модель записи локально и чтения глобальнозначительно сокращает использование дорогостоящей полосы пропускания и уменьшает задержку, что приводит к живым комментариям в реальном времени [12]. Таким образом, для реализации живых комментариев используйте основанную на push модель записи локально и чтения глобально.

Прототип службы комментариев в реальном времени

Клиенты, просматривающие живое видео, называются получателями, а клиенты, публикующие комментарии к живому видео, называются издателями. Сервер, поддерживающий соединения SSE с приемниками, называется шлюзом сервером. Для сохранения жизненного цикла клиентских подключений можно выделить выделенное хранилище подписки на диске. диспетчер — это уровень абстракции для публикации объектов данных, таких как живые комментарии или лайки Facebook, выполненные клиентом. При публикации живого комментария на живом видео выполняются следующие операции [17]:

  1. приемник подписывается на живое видео на сервере шлюза
  2. сервер шлюза сохраняет метаданные клиентского соединения в хранилище подписки
  3. издатель публикует живой комментарий к живому видео
  4. Диспетчер запрашивает в магазине подписок все ассоциации просмотра видео в реальном времени.
  5. диспетчер публикует комментарий в режиме реального времени на сервер шлюза
  6. сервер шлюза доставляет комментарий в реальном времени всем подписавшимся получателям

Сервер шлюза поддерживает соединения SSE с клиентами. Хранилище подписки должно быть реплицировано не менее трех раз для обеспечения надежности и долговечности [17]. Хранилище подписки станет узким местом для масштабирования службы комментариев в реальном времени, поскольку каждая операция чтения и записи комментариев в реальном времени должна запрашивать хранилище подписки. Вдобавок ко всему, клиентские подключения к конкретному живому видео недолговечны, потому что клиенты прокручивают ленту новостей Facebook. В результате хранилище подписки должно часто обновляться. Таким образом, текущий прототип не будет соответствовать требованиям масштабируемой и надежной службы комментариев в реальном времени.

Распространение живых комментариев

Получатель может периодически запрашивать (на основе запроса) сервер, чтобы проверить, были ли опубликованы новые живые комментарии. Периодический опрос сервера приведет к пустым ответам, потребляющим ненужную полосу пропускания. Кроме того, интервал опроса для живых комментариев должен быть меньше 250 мс для работы в режиме реального времени. Увеличение частоты опроса, скорее всего, приведет к перегрузке серверов. Подход на основе push-уведомлений с использованием постоянных соединений SSE оптимален для доставки живых комментариев получателям в режиме реального времени [12].

Соединения SSE с клиентом являются неблокирующими по принципу запустил и забыл для повышения производительности [8]. Хранилище подписок в памяти с использованием Redis может быть подготовлено локально на сервере шлюза для хранения однозначных ассоциаций между приемниками и живым видео Facebook. Тип данных sets в Redis можно использовать для эффективного хранения эфемерных ассоциаций просмотров. Хранилище подписки запрашивается для определения получателей, которые должны получать живые комментарии к конкретному живому видео [12].

Абстрактная и масштабируемая платформа реального времени может быть создана для работы в режиме реального времени, например для отображения присутствия в реальном времени, push-уведомлений, лайков Facebook, реакций Facebook, индикаторов набора текста и комментариев в реальном времени. Платформу реального времени можно повторно использовать на нескольких платформах, таких как Facebook, Messenger, Instagram, Twitch, TikTok, LinkedIn или YouTube [17]. Должна быть реализована динамически настраиваемая архитектура системы плагинов для создания абстрактной платформы реального времени. Плагины представляют собой встроенный исполняемый код для дополнительной логики, которая вызывается при определенных событиях, таких как публикация живого комментария [3]. Принцип построения масштабируемой распределенной системы заключается в том, чтобы начать с малого и постепенно добавлять в архитектуру простые уровни [8]. Общие рекомендации по горизонтальному масштабированию сервиса следующие [26]:

  • оставить службу без состояния
  • разделить службу
  • копировать услугу

Комментирование в реальном времени с сервером Pub-Sub

Паттерн публикация-подписка (pub-sub) позволяет службам, работающим на разных технологиях, взаимодействовать друг с другом [13]. Техника pub-sub, использующая шину сообщений, позволяет производителю отправлять живые комментарии (сообщения) нескольким потребителям. Сервисы могут взаимодействовать друг с другом мгновенно, без заданных интервалов для опроса данных из соответствующих источников данных в реактивной архитектуре [14].

Использование Apache Kafka в качестве сервера Pub-Sub

Apache Kafka настроен как сервер публикации и подписки, чтобы разделить производителей и потребителей. Службу комментариев в реальном времени можно надежно масштабировать за счет разделения задач. Протокол потоковой передачи Kafka предлагает более низкие накладные расходы на сообщение и обеспечивает гарантии порядка, гарантии целостности и идемпотентность. Кроме того, протокол потоковой передачи Kafka позволяет сегментировать данные перед их потоковой передачей потребителям [4], [8]. Системный дизайн живых комментариев сложен по следующим причинам:

  • клиенты будут постоянно прокручивать ленту новостей Facebook
  • живые видео, видимые в окне просмотра клиента, будут часто меняться

Будет несколько получателей (потребителей),просматривающихживое видео. Прямые комментарии, опубликованные в прямом эфире, должны потребляться всеми получателями. Когда клиент прокручивает ленту новостей Facebook или выходит за пределы панели комментариев, клиент больше не должен видеть живые комментарии. Потребитель должен динамически отказаться от подписки на определенное живое видео (тема Kafka), чтобы перестать видеть живые комментарии к определенному живому видео. Однако отписаться от темы Кафки — дорогостоящая операция [21], [22]. Кроме того, отслеживание серверов шлюзов, потребляющих определенные живые видео, нетривиально, что приводит к превышению подписки на все темы Kafka. Ограничения использования Apache Kafka для создания сервиса живых комментариев можно резюмировать следующим образом [8]:

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

Темы Kafka могут быть разделены по идентификаторам потоков за счет несбалансированных разделов. Потребители могут подписаться на определенные разделы, используя согласованное хеширование [3]. Тем не менее, потребители не будут знать заранее идентификаторы потоков, которые будут интересны клиентам. В типичном сценарии у сервера будет разнообразный набор клиентов, заинтересованных почти во всех видео в прямом эфире, что приводит к подписке на все видео в прямом эфире. Apache Pulsar также сталкивается с тем же набором проблем, что и Apache Kafka, в случае использования службы комментариев в реальном времени [8]. Таким образом, не используйте Apache Kafka в качестве сервера публикации и подписки для реализации живых комментариев.

Использование Redis в качестве сервера Pub-Sub

Клиенты должны использовать протокол с именем RESP (протокол сериализации REdis) для связи с сервером Redis. Redis можно использовать как сервер pub-sub для передачи живых комментариев (сообщений) между узлами [2]. Кластер Redis можно использовать для масштабирования сервера pub-sub. Сервер публикации и подписки Redis реплицирует каждое входящее сообщение на все узлы, поскольку Redis не знает набор клиентов (получателей) на конкретном узле, что приводит к снижению производительности. Последовательный алгоритм хеширования используется для балансировки нагрузки клиентских подписок на живое видео между набором независимых узлов Redis для улучшения масштабируемости. Последовательное хеширование значительно сокращает перемещение активных соединений между узлами при сбое или добавлении узла. С точки зрения непрофессионала, входящие сообщения публикуются на всех узлах Redis, но сервер прослушивает только небольшой набор узлов для конкретной подписки (живое видео) для улучшения масштабируемости. Реплики Redis должны быть предоставлены на каждом узле для выполнения автоматического аварийного переключения при сбое узла [3].

TCP-соединение поддерживается между производителем и Redis, а Redis — потребителем для доставки сообщений. В качестве нетрадиционного обходного пути сервер может разорвать TCP-соединение между узлом-потребителем и сервером pub-sub для отмены подписки потребителя на тему (живое видео), когда клиент прокручивает ленту новостей Facebook. Недостатки использования pub-sub сервера Redis для службы комментариев в реальном времени заключаются в следующем:

  • не гарантируется хотя бы однократная доставка сообщения
  • надежность доставки сообщений зависит от TCP-соединения
  • сообщения могут быть потеряны из-за отсутствия постоянства сообщений

В заключение, не используйте серверное решение Redis pub-sub для реализации живых комментариев.

Использование Redis Streams в качестве сервера Pub-Sub

Поток Redis — это структура данных, которую можно использовать как журнал только для добавления для повышения скорости отклика системы. Проще говоря, потоки Redis функционально очень похожи на Apache Kafka. Потоки Redis в качестве pub-sub сервера предлагают следующие преимущества [23], [24], [14], [19], [25], [20]:

  • гарантированная как минимум однократная доставка сообщений
  • надежные сообщения благодаря сохранению с помощью методов сохранения только с добавлением файла (AOF) или базы данных Redis (RDB).
  • более терпим к сетевым разделам
  • улучшенная задержка благодаря эффективному хранилищу в памяти
  • относительно тривиально для обеспечения и эксплуатации
  • сообщения могут потребляться либо блокирующими, либо неблокирующими методами

Конфигурация Redis Sentinel или Redis Active-Active обеспечивает высокую доступность кластера за счет повышенной операционной сложности. Redis Sentinel — это распределенная система, которая отслеживает и предоставляет политики аварийного переключения для экземпляров Redis. Потоки Redis могут динамически отменять подписку потребителей на определенную тему (видео в прямом эфире). Однако существует риск потери данных в потоках Redis, поскольку данные только периодически записываются на диск. Ограничение памяти также может быть узким местом для создания масштабируемой службы комментариев в реальном времени [19], [20]. В заключение, не используйте потоки Redis в качестве серверного решения pub-sub для реализации живых комментариев.

Абстрактная платформа реального времени

Хранилище подписок в памяти можно настроить с помощью Redis на том же компьютере, где работает сервер шлюза. Хранилище подписки в памяти будет содержать сопоставление между живыми видео и приемниками (клиентскими подключениями). Сервер шлюза может выполнять регулирование и включать дополнительную логику продукта. Выделенное хранилище конечных точек на диске можно использовать для определения серверов шлюза, заинтересованных в конкретном живом видео. Хранилище конечной точки будет содержать сопоставление между видео в реальном времени и набором серверов шлюза. При публикации живого комментария на живом видео выполняются следующие операции [17]:

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

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

  • сервер шлюза может отправлять периодические пульсирующие сигналы на приемники, чтобы проверить, подключены ли они еще
  • Ключи Redis TTL могут истечь сроком действия записи о подписке, когда сигналы пульса показывают сбой
  • логика JavaScript на стороне клиента может инициировать событие, когда клиент прокручивает видео в реальном времени.

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

Дополнительные учебные ресурсы по проектированию систем

Вы готовитесь к собеседованию по системному дизайну и чувствуете себя ошеломленным сложностью процесса? Вы хотите получить знания и уверенность, чтобы успешно пройти собеседование и продвинуться по карьерной лестнице в области разработки программного обеспечения? Тогда вам обязательно нужно пройти курс Собеседование по системному дизайну от DesignGurus!

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

Не упустите эту возможность поднять свои навыки проектирования систем на новый уровень и достичь своих карьерных целей. Запишитесь на курс Собеседование по проектированию систем от DesignGurus сегодня и сделайте первый шаг к успеху!

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

Живой опыт поддерживается API-интерфейсами, управляемыми событиями в реальном времени, для удовлетворения требований клиентов. Платформа реального времени основана на низкой задержке, целостности данных, отказоустойчивости, доступности и масштабируемости [2].

Как сервер шлюза управляет клиентскими подключениями?

Актер — это чрезвычайно легкий объект, который может получать сообщения и выполнять действия по обработке сообщений. Актер отделен от источника сообщения. Актер отвечает только за распознавание типа полученного сообщения и выполнение необходимого действия. Поток будет назначен актеру, когда сообщение должно быть обработано. Поток освобождается после обработки сообщения и назначения потока следующему действующему лицу. Общее количество потоков будет пропорционально количеству ядер ЦП. Относительно небольшое количество потоков может обрабатывать значительное количество одновременно работающих акторов, потому что поток назначается актору только во время выполнения [8], [9], [11].

Постоянное клиентское соединение SSE может быть назначено актеру. Когда актор получает сообщение, обработчик на акторе определяет, как сообщение будет опубликовано в клиентском соединении. Программирование в стиле акторов может быть реализовано с помощью фреймворков акторов, таких как Akka (Java, Scala), Pykka (Python) или Cloudi (Erlang) для создания высокопараллельных, распределенных и устойчивых приложений, управляемых сообщениями. 8], [9], [11].

(Дочерние) акторы управляются супервизором. Следующие операции выполняются, когда оперативный комментарий публикуется диспетчером [8]:

  1. диспетчер публикует комментарий в режиме реального времени для супервизора на сервере шлюза по протоколу HTTP.
  2. актер-супервайзер транслирует комментарий в прямом эфире всем дочерним актерам
  3. дочерние акторы используют дескриптор подключения для пересылки живого комментария получателям

Клиент (получатель) будет отображать полученный живой комментарий в веб-браузере.

Как обрабатывать живые комментарии к нескольким живым видео?

К одному и тому же серверу шлюза может быть подключено несколько клиентов, которые смотрят разные живые видео. Актеры на сервере шлюза не должны публиковать прямой комментарий к определенному живому видео для всех подключенных клиентов, потому что некоторые клиенты могут смотреть другое живое видео. На рисунке 17 клиенты, подключенные к одному и тому же серверу шлюза, просматривают разные живые видео с идентификаторами живого видео синий и красный. Когда клиент публикует комментарий к прямой трансляции с идентификатором синий в качестве идентификатора, только клиенты, просматривающие прямую трансляцию с синим в качестве идентификатора, должны иметь возможность просматривать опубликованный комментарий. живой комментарий.

Хранилище подписок в памяти с использованием Redis можно подготовить для работы на том же компьютере, где работает сервер шлюза. Клиент, начинающий смотреть живое видео, подписывается на определенное живое видео на сервере шлюза через HTTP. Хранилище подписки будет хранить в памяти сопоставление между видео в реальном времени и набором приемников. Хранилище подписки в памяти используется сервером шлюза для хранения ассоциативности просмотров по следующим причинам [8]:

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

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

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

Локальное хранилище подписок в памяти позволяет эффективно публиковать живые комментарии к нескольким живым видео.

Как поддерживать массовые параллельные клиенты для нескольких прямых трансляций?

Сервер шлюза должен быть разделен путем подготовки нескольких экземпляров серверов шлюза для обработки большого количества одновременных клиентов, как показано на рис. 21. Дополнительная служба, известная как диспетчер, предназначена для трансляции комментариев в режиме реального времени между несколькими серверами шлюза [8]. ]. Клиенты, подключенные к серверу шлюза, могут просматривать только часть видео в реальном времени. Для диспетчера неэффективно транслировать прямые комментарии ко всем видео в реальном времени на все серверы шлюзов, поскольку некоторые серверы шлюзов могут быть подписаны только на подмножество видео в реальном времени.

Например, на рис. 22 клиенты, подключенные к серверу шлюза 1, смотрят только живое видео с синим в качестве идентификатора видео. Когда диспетчер публикует живые комментарии к живому видео с красным в качестве идентификатора видео на сервер шлюза 1, опубликованные живые комментарии будут обработаны и впоследствии проигнорированы сервером шлюза 1, что приведет к напрасной трате вычислительных ресурсов. Диспетчер не должен публиковать живые комментарии ко всем живым видео на всех серверах шлюза для повышения производительности [8].

Хранилище конечных точек в памяти, использующее Redis, может быть настроено для локального запуска на том же компьютере, что и диспетчер. Клиент, начинающий смотреть живое видео, подписывается на сервер шлюза через HTTP. Хранилище подписки на сервере шлюза будет сохранять сопоставление между конкретным видео в реальном времени и набором приемников. Сервер шлюза подпишется на конкретное живое видео в диспетчере через HTTP. Хранилище конечных точек в памяти диспетчера будет хранить сопоставление между видео в реальном времени и набором подписанных серверов шлюза. Хранилище конечных точек в памяти используется диспетчером для хранения данных подписки серверов шлюза по следующим причинам [8]:

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

С точки зрения непрофессионала, хранилище конечных точек в памяти в диспетчере используется для идентификации подписки на живое видео серверами шлюза для повышения производительности.

Следующие операции выполняются, когда клиент публикует живой комментарий к живому видео с red в качестве идентификатора [8]:

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

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

Если вы планируете подписаться на Medium, используя мою реферальную ссылку, я хотел бы сообщить вам, что я получу часть членских взносов в качестве вознаграждения за то, что пригласил вас. Это помогает мне продолжать создавать ценный контент. Однако я хочу заверить вас, что это никак не повлияет на стоимость вашей подписки. Вы по-прежнему получите те же преимущества и возможности, что и любой другой участник Medium. Спасибо, что рассмотрели мою реферальную ссылку и поддержали мою работу!

Масштабирование комментариев в режиме реального времени для обработки пиковой нагрузки

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

Хранилище пар "ключ-значение" на диске, известное как хранилище конечных точек, может сохранять связи подписки между видео в реальном времени и набором серверов шлюза. Хранилище конечной точки должно быть реплицировано для обеспечения масштабируемости и высокой доступности. Хранилище конечной точки должно сохранять только подписку сервера шлюза, локальную по отношению к центру обработки данных. Для подготовки хранилища конечных точек можно использовать DynamoDB, Redis или MongoDB. Кроме того, хранилище конечных точек обеспечивает надежность в случае сбоя диспетчера. Любой диспетчер сможет идентифицировать набор подписанных серверов шлюзов, запросив внешнее хранилище конечных точек [8].

Диспетчер связывается с серверами шлюза по протоколу HTTP. Любой экземпляр диспетчера сможет публиковать живые комментарии на любом сервере шлюза. Следующие операции выполняются, когда клиент публикует живой комментарий к живому видео с red в качестве идентификатора [8]:

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

Хранилище конечной точки не нужно разбивать на разделы из-за небольшого набора данных.

Что такое рабочий процесс подписки и публикации для живых комментариев?

Следующие операции выполняются для подписки, когда клиент начинает смотреть живое видео [8]:

  1. клиент подписывается на сервер шлюза через HTTP
  2. шлюз сохраняет ассоциации просмотров в хранилище подписок в памяти.
  3. сервер шлюза делает запрос на подписку в хранилище конечных точек, создавая запись в хранилище ключей и значений.

Следующие операции выполняются, когда клиент публикует живой комментарий к живому видео [8]:

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

Как развернуть службу комментариев в реальном времени в нескольких центрах обработки данных?

Служба комментариев в реальном времени должна быть развернута в нескольких центрах обработки данных (DC) для обеспечения масштабируемости и производительности. Может быть сценарий, когда ни один клиент не просматривает конкретное живое видео в некоторых центрах обработки данных. Диспетчер может запросить локальное хранилище конечных точек, чтобы определить, есть ли какие-либо серверы шлюза, подписанные на конкретное живое видео в локальном центре обработки данных. Хранилище конечных точек хранит только подписку серверов шлюза локально в центре обработки данных [8].

Например, на рис. 28 ни один клиент не просматривает видео с красным в качестве идентификатора видео в центрах обработки данных 1 и 3. Когда клиент публикует комментарий в прямом эфире с красным в качестве идентификатора видео, только диспетчер в центре обработки данных 2 должен транслировать комментарий в реальном времени на подписанные серверы шлюза. Диспетчер должен транслировать комментарий в режиме реального времени диспетчерам в одноранговых центрах обработки данных, чтобы гарантировать, что клиенты, подписавшиеся на конкретное живое видео в одноранговых центрах обработки данных, получат комментарий в режиме реального времени.

Следующие операции выполняются, когда клиент публикует живой комментарий к живому видео [8]:

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

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

  • подписка между центрами обработки данных
  • хранилище конечных точек, настроенное для всего региона

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

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

Как поддерживать индикаторы набора текста в живом видео?

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

Как отобразить общее количество комментариев к каждому живому видео?

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

Если вы планируете подписаться на Medium, используя мою реферальную ссылку, я хотел бы сообщить вам, что я получу часть членских взносов в качестве вознаграждения за то, что пригласил вас. Это помогает мне продолжать создавать ценный контент. Однако я хочу заверить вас, что это никак не повлияет на стоимость вашей подписки. Вы по-прежнему получите те же преимущества и возможности, что и любой другой участник Medium. Спасибо, что рассмотрели мою реферальную ссылку и поддержали мою работу!

Масштабируемость

Диспетчер на современном сервере может обрабатывать до 5000 запросов в секунду. Современный сервер шлюза, реализованный с использованием акторной модели, может поддерживать до 100 тысяч одновременных клиентских подключений SSE. Коэффициент умножения, введенный комбинацией серверов диспетчера и шлюза, делает службу комментариев в реальном времени чрезвычайно масштабируемой. Другой коэффициент умножения находится внутри сервера шлюза с использованием модели акторов. Модель актора позволяет повторно использовать пул потоков для увеличения пропускной способности. Службу комментариев в реальном времени можно масштабировать по горизонтали, предоставив новые серверы [8], [11], [16].

Балансировщики нагрузки должны быть введены между различными уровнями системы для масштабируемости и повышения отказоустойчивости. Сервисы должны использовать полную мощность серверов для масштабируемости [15]. Автомасштабирование можно включить для удовлетворения эластичного спроса и борьбы с потенциальными всплесками трафика. Горизонтальное масштабирование сопряжено со сложной архитектурой и увеличением затрат на инфраструктуру и обслуживание [2].

Задержка

Служба комментариев в реальном времени развертывается в нескольких центрах обработки данных, чтобы клиенты находились ближе к серверам и уменьшали задержку. Apache Samza можно использовать для измерения сквозной задержки микросервисной системы живых комментариев [10]. Задержка службы живых комментариев довольно низкая по следующим причинам [2], [8], [15]:

  • есть только один поиск значения ключа из (дискового) хранилища конечной точки
  • есть только один поиск в памяти из магазина подписки
  • сетевых переходов очень мало

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

параллелизм

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

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

Блокировку чтения-записи можно использовать для обновления набора серверов-шлюзов, подписанных на конкретное живое видео в магазине конечной точки. Клиент, подписывающийся на сервер шлюза, обрабатывается хранилищем подписок в памяти, работающим на Redis. Проблемы параллелизма решаются автоматически, поскольку операции в Redis являются атомарными и однопоточными [14].

Высокая доступность

Шаблон трафика для живых комментариев к чрезвычайно популярным видео в прямом эфире обычно очень крутой от начальной фазы до конца живого видео, и впоследствии трафик падает до нуля. С точки зрения непрофессионала, шаблон трафика для живых комментариев к видео в прямом эфире является колючим. Когда живое видео становится чрезвычайно популярным, количество одновременных клиентов, подписавшихся на просмотр живых комментариев, увеличивается, вызывая проблему громоподобного стада [16]. Проблема громоподобного стада от живых комментариев к живому видео может быть решена с помощью следующих операций [17], [16]:

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

Шторм повторного подключения клиентских SSE-подключений к шлюзовым серверам при развертывании и событиях масштабирования можно предотвратить, настроив развертывание так, чтобы существующие серверы сохранялись в течение нескольких часов после завершения, чтобы позволить большинству существующих клиентских SSE-подключений закрыться естественным образом. . Недостатком этого подхода является относительно более медленное развертывание [3]. Службы должны работать в режиме «горячий-горячий» с аварийным переключением для обеспечения высокой доступности. Операции чтения-записи могут выполняться обоими экземплярами для масштабируемости [1].

Отказоустойчивость

Служба комментариев в реальном времени должна быть протестирована под нагрузкой для выявления сбоев в оценке пропускной способности [16]. Ограничители скорости могут использоваться для снижения нагрузки со стороны сервиса при ухудшении качества обслуживания или перегрузке трафика [3]. Для всех служб должен быть настроен высокий уровень наблюдаемости посредством мониторинга и оповещения [3], [15]. Служба комментариев в реальном времени должна быть развернута в нескольких центрах обработки данных в одном регионе и в нескольких регионах по всему миру для повышения отказоустойчивости. Развертывание службы комментариев в реальном времени в нескольких центрах обработки данных по всему миру сопряжено с трудностями, связанными с увеличением количества инженерных работ, усилий DevOps и затрат на инфраструктуру [2].

Разработка и развертывание

Инфраструктуру как код можно использовать для предоставления серверов на «голом железе». Непрерывная интеграция и непрерывная доставка/непрерывное развертывание (CI/CD) должны быть настроены для ускорения циклов разработки [15].

Долговечность

Живые комментарии должны быть сохранены в базе данных NoSQL, такой как Apache Cassandra, для долговечности. Процесс уплотнения в Apache Cassandra для удаления надгробий со временем может стать потенциальным узким местом производительности. База данных может быть настроена в разных кластерах для разных профилей использования данных, чтобы преодолеть ограничение. Например, для разных нормативных требований (HIPAA, GDPR) могут быть установлены разные правила репликации. Моментальные снимки базы данных должны делаться через определенные промежутки времени для восстановления данных. Данные могут быть реплицированы на несколько гиперскейлеров в одном и том же регионе для обеспечения надежности [1].

Следующие операции выполняются, когда клиент публикует живой комментарий к живому видео:

  1. балансировщик нагрузки распространяет опубликованный клиентом комментарий в режиме реального времени любому случайному диспетчеру по HTTP
  2. диспетчер записывает живой комментарий в хранилище комментариев (NoSQL) для постоянного хранения
  3. диспетчер запрашивает хранилище конечных точек для определения набора серверов шлюзов, подписанных на конкретное живое видео.
  4. диспетчер пересылает комментарий в режиме реального времени на набор подписанных серверов шлюза по протоколу HTTP.
  5. серверы шлюза запрашивают локальное хранилище подписки в памяти, чтобы идентифицировать клиентов, подписавшихся на конкретное живое видео.
  6. серверы шлюза транслируют живые комментарии подписанным клиентам через SSE

Операционная сложность

Полностью управляемые сервисы, размещенные на гиперскейлерах, таких как AWS, могут снизить сложность работы службы комментариев в реальном времени. В качестве альтернативы диспетчер в службе комментариев в реальном времени может быть реализован с бессерверными функциями. Преимущества использования бессерверных функций следующие [2]:

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

Краткое содержание

Живой комментарий — популярный вопрос на собеседовании по проектированию систем. Платформа реального времени, созданная для публикации комментариев в реальном времени, может отображать реакции Facebook, лайки, количество одновременных просмотров, статус присутствия пользователя, онлайн-опросы или просмотренные квитанции. Вы можете прочитать оригинальную подробную статью, опубликованную на сайте systemdesign.one.

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

Если вы планируете подписаться на Medium, используя мою реферальную ссылку, я хотел бы сообщить вам, что я получу часть членских взносов в качестве вознаграждения за то, что пригласил вас. Это помогает мне продолжать создавать ценный контент. Однако я хочу заверить вас, что это никак не повлияет на стоимость вашей подписки. Вы по-прежнему получите те же преимущества и возможности, что и любой другой участник Medium. Спасибо, что рассмотрели мою реферальную ссылку и поддержали мою работу!

Лицензия

CC BY-NC-ND: эта лицензия позволяет повторным пользователям копировать и распространять содержимое этой статьи на любом носителе или в любом формате только в неадаптированной форме, в некоммерческих целях и только при условии указания авторства. отдано создателю. Оригинальная статья должна иметь обратную ссылку.

Рекомендации

[1] Тодд Грин, Эффективное управление массовой инфраструктурой данных в реальном времени (2020 г.), AWS Events

[2] Мэтью О’Риордан, Использование бессерверных веб-сокетов для обеспечения обмена сообщениями в реальном времени (2022 г.), infoq.com

[3] Дима Забелло, Кайл Максвелл и Саураб Шарма, Новый сервис Reddit в режиме реального времени, reddit.com

[4] Мэтью О'Риордан, Проблемы построения надежной экосистемы, управляемой событиями в реальном времени (2020 г.), infoq.com

[8] Ахилеш Гупта, Миллион лайков в секунду: взаимодействие в реальном времени в прямом эфире, infoq.com

[9] Ахилеш Гупта, Обмен мгновенными сообщениями в LinkedIn: масштабирование до сотен тысяч постоянных подключений на одной машине (2016), engineering.linkedin.com

[10] Макс Вольф, Samza Aeon: анализ задержки для асинхронных односторонних потоков (2018 г.), engineering.linkedin.com

[11] Ахилеш Гупта, Теперь ты меня видишь, а теперь нет: платформа LinkedIn для присутствия в реальном времени (2018), engineering.linkedin.com

[12] Кен Дитер, Живое комментирование: за кулисами (2011), engineering.fb.com

[13] Аджит Райна, Как создать чат-приложение с помощью Redis, developer.redis.com

[14] Фернандо Доглио, Использование Redis для чата и обмена сообщениями (2022), memurai.com

[15] Тодд Хофф, Архитектура прямой трансляции видео Justin.Tv (2010), highscalability.com

[16] Тодд Хофф, Как Facebook транслирует прямые трансляции для 800 000 одновременных зрителей (2016), highscalability.com

[17] Джефф Барбер, Создание инфраструктуры реального времени в Facebook (2017), usenix.org

[18] Использование событий, отправленных сервером, mozilla.org

[19] Фернандо Доглио, Apache Kafka против Redis Streams (2021), memurai.com

[20] Antirez, Потоки: новая структура данных общего назначения в Redis, antirez.com

[21] Смещение минут хранения в Kafka, kafka.apache.org

[22] Управление группами потребителей в Kafk, docs.confluent.io

[23] Потоки Redis, redis.io

[24] Объяснение потоков Redis (2021), youtube.com

[25] Постоянство Redis, redis.io

[26] Веб-масштабируемость для стартап-инженеров Артура Эйсмонта (2015)

[27] Что такое прямой эфир?, cloudflare.com

Повышение уровня кодирования

Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:

  • 👏 Хлопайте за историю и подписывайтесь на автора 👉
  • 📰 Смотрите больше контента в публикации Level Up Coding
  • 💰 Бесплатный курс собеседования по программированию ⇒ Просмотреть курс
  • 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"

🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу