Pushy — это WebSocket‑сервер Netflix, который поддерживает долговременные WebSocket‑соединения с устройствами, на которых работает приложение Netflix. Благодаря этому данные с бэкенд‑сервисов можно отправлять на устройства по мере необходимости. При таком подходе нет нужды в постоянного опроса сервисов устройствами. За последние несколько лет Pushy пережил огромный рост, превратившись из сервиса для негарантированной доставки сообщений в неотъемлемую часть экосистемы Netflix. В этом материале вы узнаете о том, как мы развивали и масштабировали сервер Pushy, стремясь к тому, чтобы он хорошо справлялся со своими текущими обязанностями, и к тому, чтобы подготовить его к будущим нагрузкам. Он поддерживает сотни миллионов одновременных WebSocket‑подключений, доставляет адресатам сотни тысяч сообщений в секунду и удерживает стабильный уровень надёжности доставки сообщений в 99,999%.
История Pushy и причины его создания
То, что мы приступили к разработке Pushy и начали использовать этот сервер, обусловлено двумя основными причинами, представленными определёнными сценариями использования наших систем. Первым таким сценарием было голосовое управление. Это — когда можно запустить воспроизведение видео или инициировать поиск чего‑либо с помощью виртуального ассистента, дав ему голосовую команду вроде «Покажи мне „Очень странные дела“ на Netflix». (Если хотите сами это попробовать — взгляните на эту статью!).
Если рассмотреть применение в этой ситуации голосового помощника Alexa — можно разобраться с тем, как мы всё это сделали благодаря партнёрству с Amazon. После того, как Alexa получает голосовую команду, мы позволяем Amazon сделать аутентифицированное обращение к нашему внутреннему голосовому сервису. Обращение проводится через сервис apiproxy, реализуемый нашим стриминговым пограничным прокси‑сервером. В состав материалов этого обращения входят метаданные — такие, как сведения о пользователе, и подробные сведения о команде, например — данные о сериале, который нужно включить. Затем голосовой сервис конструирует сообщение для устройства и помещает его в очередь сообщений. Эта очередь обрабатывается, сообщение передаётся Pushy, который и отправляет его на устройство. Наконец — устройство получает сообщение и выполняет запрошенное действие, вроде включения сериала. Изначально этот функционал был разработан для устройств Amazon Fire TV, а после этого мы занялись его расширением на другие устройства.
Вторая причина появления Pushy — это RENO (Rapid Event Notification System) — система быстрого оповещения о событиях, принципы работы которой мы описывали в самом начале статьи. До интеграции этой системы с Pushy ПО телевизора приходилось постоянно отправлять нашему бэкенд-сервису опрашивающие запросы, цель которых заключается в том, чтобы узнать — есть ли в распоряжении сервиса какие-то новые данные для устройства. Такие запросы делались каждые несколько секунд, что означало, во-первых — необходимость обработки сервисом большого количества внешних запросов, а во-вторых — серьёзную дополнительную нагрузку на устройства, ресурсы которых часто были ограничены. Интеграция RENO с WebSockets и Pushy улучшила ситуацию и для сервиса, и для устройств, позволив сервису отправлять устройствам обновлённые сведения по мере готовности данных. Это привело к снижению частоты запросов и к экономии ресурсов.
Подробности об истории Pushy вы можете найти в этом видео, записанном на конференции InfoQ Dev Summit Boston в 2018 году. Со времён той презентации Pushy вырос — как в плане размеров, так и в плане обязанностей, возлагаемых на этот сервер.
Расскажем о тех усилиях, которые мы приложили к развитию Pushy и к оснащению его новыми возможностями.
Охват клиентских устройств
Изначально новые возможности по обмену сообщениями были нацелены на устройства следующих видов: Fire TV, PS4, Samsung TV и LG TV. Это означало, что речь шла о примерно 30 миллионах устройств, участвующих в испытании. Видя те очевидные преимущества, которые нам даёт Pushy, мы продолжили работу над его функционалом в расчёте на более широкий диапазон устройств. Для них применение Pushy давало те же плюсы, о которых мы говорили. По состоянию на сегодняшний день мы расширили список устройств, участвующих в испытании. Сейчас речь идёт о примерно миллиарде устройств, в число которых входят и мобильные устройства, на которых работает приложение Netflix. Pushy используется и на нашей веб-платформе. Мы, кроме того, включили в число поддерживаемых платформ и более старые устройства, не поддерживающие современные возможности, вроде TLS и HTTPS-запросов. В случае с такими устройствами мы реализовали защищённую схему взаимодействия клиента и Pushy посредством уровня шифрования/расшифровки, применяющемся на каждой стороне взаимодействия. Это позволяет организовать обмен конфиденциальными сообщениями между устройствами и серверами.
Масштабирование Pushy с учётом роста нагрузки (и не только)
Рост
Учитывая то, о каком количестве и разнообразии устройств идёт речь, у Pushy стало гораздо больше дел. За последние пять лет нагрузка на Pushy выросла с десятков миллионовдо сотен миллионов одновременных соединений. Этот сервер регулярно достигает показателя в 300 000 сообщений, отправленных в секунду. Для того чтобы поддержать такой рост, мы пересмотрели изначальные проектные решения и идеи, лежащие в основе Pushy, ориентируясь и на его будущую роль в нашей инфраструктуре, и на стабильность его работы. Pushy в последние годы работал, почти не требуя нашего вмешательства. И мы, улучшая его в расчёте на его растущую важность, стремились, кроме прочего, к тому, чтобы привести его в стабильное состояние, которое позволило бы ему спокойно работать ещё несколько лет. Это особенно важно из-за того, что мы развивали другие части нашей платформы, опирающиеся на Pushy. Устойчивая и стабильная основа нашей инфраструктуры позволяет нашим партнёрам с уверенностью продолжать разработку своих решений на основе Pushy.
В ходе этой эволюции мы смогли поддерживать высокий уровень доступности сервиса и устойчивые показатели скорости доставки сообщений. А именно, в последние несколько месяцев Pushy показывает уровень надёжности доставки сообщений в 99,999%. Когда нашему партнёру надо доставить сообщение на устройство — наша задача заключается в том, чтобы обеспечить ему такую возможность.
Рассмотрим ещё некоторые направления, в которых мы развивали Pushy, ориентируясь на растущие масштабы его применения.
Обработчик сообщений
Асинхронный обработчик сообщений (Message Processor на схеме) — это один из аспектов нашей инфраструктуры, развитием которого мы занимались. Предыдущая версия обработчика сообщений представляла собой задание Mantis, занимающееся потоковой обработкой данных из очереди сообщений. Это была весьма эффективная система, в работе которой, однако, использовались заранее заданные параметры задания. Это требовало ручного вмешательства в том случае, если мы нуждались в горизонтальном масштабировании системы, и в том случае, когда мы выпускали её новую версию.
Обработчик сообщений верой и правдой служил Pushy многие годы. Но по мере того, как росло количество обрабатываемых сообщений, по мере того, как мы вносили в код обработчика всё больше изменений, мы поняли, что нам нужно что-то более гибкое. В частности — нас интересовали кое-какие возможности, которые очень нам нравились в других применяемых нами сервисах. Среди них — автоматическое горизонтальное масштабирование, поддержка канареечных релизов, автоматизированное применение стратегии развёртывания, известной как «red/black rollout», улучшение наблюдаемости инфраструктуры. Учитывая вышесказанное, мы переписали обработчик сообщений, сделав его самостоятельным сервисом Spring Boot и использовав проверенные компоненты Netflix. Новый сервис занимается тем же самым, что и старый, но отличается следующими полезными возможностями: облегчённое развёртывание, применение канареечных конфигураций, позволяющих безопасно выпускать обновления, поддержка политик автоматического масштабирования, которые позволяют сервису подстраиваться под разные объёмы работ.
Переписывание чего-либо — это всегда риск. И мы, когда чем-то недовольны, никогда не стремимся начинать улучшения именно с переписывания, особенно когда речь идёт об уже используемых и хорошо работающих системах. В данном случае мы столкнулись с ростом нагрузки, связанной с поддержкой и улучшением нашего собственного задания для потоковой обработки данных. Поэтому мы и приняли такое неоднозначное решение, как переписывание соответствующего сервиса. Отчасти это было так из-за того, что обработчик сообщений играл чёткую и понятную роль в нашей инфраструктуре. Речь шла не о переписывании огромного монолитного сервиса, а о воссоздании компонента с чётким набором обязанностей. Наличие этого компонента в системе преследовало конкретную цель, существовали понятные критерии успешности его работы. Кроме того, мы ясно видели путь улучшения обработчика сообщений. С тех пор, как в середине 2023 года мы переписали обработчик, этот компонент вообще перестал требовать нашего внимания, спокойно и надёжно работая самостоятельно.
Push-реестр
Большую часть своей жизни Pushy, для хранения метаданных (сведений о соединениях) в своём Push-реестре (Push Registry на схеме) использовал Dynomite. Dynomite — это опенсорсная обёртка для Redis, разработанная в Netflix. Она даёт пользователям Redis некоторые дополнительные возможности. Среди них — автоматический шардинг и автоматическая межрегиональная репликация данных. Кроме того, Dynomite помогает Pushy работать с низкими задержками и предоставляет простые механизмы управления сроками истечения действия записей. И то и другое чрезвычайно важно для успешной работы Pushy.
По мере того, как рос круг обязанностей Pushy, мы начали сталкиваться с некоторыми сложностями, связанными с Dynomite. Эта система отличается отличной производительностью, но, по мере роста всей системы, настройки, касающиеся её масштабирования, нужно вводить вручную. Наши товарищи из команды Cloud Data Engineering (CDE) — те, которые создали внутреннюю систему работы с данными Netflix, любезно помогали нам масштабировать Dynomite и выполнять кое-какие настройки. Но всё это, в итоге, с ростом системы, стало очень уж сложно.
Усиление этих проблемных моментов совпало с появлением KeyValue — новой системы хранения данных от команды CDE, нацеленной на разработчиков Netflix, которую, в двух словах, можно описать как «Хеш-таблица как сервис». KeyValue — это абстракция над самим движком хранения данных, которая позволяет нам выбирать наилучший движок, соответствующий нашим нуждам относительно целей уровня обслуживания (Service Level Objectives, SLO) системы. В нашем случае особую ценность имеет низкий уровень задержек при обработке данных. Чем быстрее мы можем прочитать данные из KeyValue — тем быстрее соответствующие сообщения могут быть доставлены адресатам. С помощью команды CDE мы перевели наш Push-реестр на KeyValue. То, что в итоге получилось, нам очень понравилось. После того, как мы настроили хранилище под нужды Pushy, никаких вмешательств в его работу уже не требовалось. Оно работает само, адекватно масштабируясь при росте нагрузки и обслуживая наши запросы с очень низким уровнем задержек.
Горизонтальное и вертикальное масштабирование Pushy
Большинство других сервисов, которые поддерживает наша команда, вроде apiproxy или стримингового пограничного прокси-сервера, зависят от производительности CPU. Когда мы сталкиваемся с ростом уровня использования процессора — срабатывает политика, которая горизонтально масштабирует эти сервисы. Это хорошо соотносится с особенностями их рабочих нагрузок. Чем больше HTTP-запросов они получают — тем больше ресурсов CPU им нужно. Мы же, видя изменение нагрузки на них, можем гибко их масштабировать — как в сторону роста, так и в сторону уменьшения выделяемых им мощностей.
А Pushy отличается несколько иными показателями, определяющими его производительность. Каждый его узел поддерживает множество подключений и доставляет сообщения по мере необходимости. В случае с Pushy уровень использования CPU пребывает на постоянно низком уровне, так как большинство подключений бездействует, ожидая передачи сообщений. Вместо того, чтобы, при масштабировании Pushy, ориентироваться на CPU, мы смотрим на количество соединений. Мы, при достижении определённого порогового значения, применяем стратегию экспоненциального масштабирования, делая так для того, чтобы быстрее повышать уровень возможностей Pushy. Принимая исходные HTTP-запросы, мы, устанавливая соединения, используем балансировку нагрузки. Так же мы применяем протокол для организации переподключения устройств, благодаря которому устройства переподключаются примерно каждые 30 минут, с некоторыми вариациями этого времени, что даёт нам стабильный поток переподключающихся устройств, позволяющий сбалансированно распределить подключения между всеми доступными экземплярами Pushy.
В течение нескольких лет наша политика масштабирования работала так: мы могли добавить новый экземпляр сервера тогда, когда среднее количество подключений на один экземпляр достигало 60 000. Для пары сотен миллионов устройств это означало необходимость постоянной работы тысяч экземпляров Pushy. При таком подходе мы можем, сколько душе угодно, горизонтально масштабировать Pushy, но всё это может довольно‑таки дорого стоить. Кроме того, нам могло бы понадобиться организовать дальнейшее разбиение Pushy на более мелкие сегменты, чтобы обойти ограничения, связанные с лимитом сетевых соединений. Это направление развития Pushy хорошо сочеталось с нашими внутренними интересами, касающимися повышения экономической эффективности систем. Мы воспользовались этой возможностью для того, чтобы пересмотреть, с учётом эффективности, идеи, высказанные ранее и лежащие в основе Pushy.
И масштабируемость, и стоимость работы системы можно было бы улучшить, увеличив количество соединений, которое может обрабатывать каждый узел Pushy. Далее — ситуация улучшилась бы при уменьшении общего количества экземпляров Pushy, при повышении эффективности работы серверов с учётом правильного баланса между типами их экземпляров, стоимостью каждого экземпляра и максимальным числом поддерживаемых ими одновременных соединений. Это, кроме того, позволило бы нам свободнее чувствовать себя в границах, очерченных лимитами сетевых соединений, снижая затраты труда на дополнительное разбиение серверов на сегменты в условиях роста нашей системы. С учётом всего этого надо сказать, что увеличение количества подключений на один узел не лишено собственных недостатков. Когда экземпляр Pushy перестаёт работать — устройства, которые были к нему подключены, сразу же пытаются переподключиться. Увеличение количества подключений на экземпляр сервера — это и увеличение количества устройств, которым может понадобиться немедленное переподключение к системе. К одному экземпляру Pushy можно было бы подключить и миллион устройств, но прекращение работы этого экземпляра приведёт к целому шквалу запросов, вызванному миллионом устройств, пытающихся одновременно переподключиться к системе.
Размышления о поиске этого хрупкого баланса привели нас к проведению глубокой оценки многих типов экземпляров серверов и подходов к настройке производительности. Стремясь к достижению этого баланса, мы, в итоге, остановились на экземплярах, способных, в среднем, поддерживать 200 000 соединений на узел. При этом мы предоставили себе некоторую свободу манёвра в виде возможности увеличения этого показателя до 400 000 соединений в том случае, если нам это понадобится. Это привело к установлению приятного баланса между использованием ресурсов — CPU и памяти, и вероятностью появления шквалов одновременно переподключающихся устройств. Мы, кроме того, расширили политики автоматического масштабирования, применив экспоненциальный подход. Чем дальше мы уходим от целевого показателя среднего количества подключений — тем больше новых экземпляров сервера мы вводим в строй. Эти улучшения позволили Pushy работать, почти не нуждаясь в наших вмешательствах, помогая ему гибко реагировать на разные ситуации, связанные с массовым подключением устройств к системе.
Надёжность и создание стабильного фундамента для системы
Наряду с вышеописанными усилиями, направленными на то, чтобы система масштабирования Pushy была бы готова к будущему росту нагрузок, мы уделили пристальное внимание и надёжности системы. Случилось это после того, как мы, занимаясь разработкой свежих возможностей, обнаружили кое‑какие пограничные случаи, связанные с подключением устройств к нашим серверам. Мы нашли несколько сфер, нуждающихся в улучшении, связанных с соединением устройств и Pushy. Речь идёт о сбоях, происходивших из‑за того, что Pushy пытался отправлять сообщения, пользуясь нерабочими соединениями, которые перестали функционировать, не уведомив об этом Pushy. В идеале чего‑то вроде «тихих сбоев» происходить не должно, но мы регулярно сталкиваемся со странным поведением клиентов, особенно — тех, что работают на достаточно старых устройствах.
Вместе с командой, отвечающей за работу с клиентами, мы смогли кое‑что улучшить. На стороне клиента улучшение работы с соединениями и переподключениями означало то, что у клиента больше шансов нормально переподключиться к системе. В Pushy мы добавили дополнительные системы проверки состояния соединения, систему очистки простаивающих соединений, систему улучшенного наблюдения за соединениями. Всё это означает то, что нам нужно было обрабатывать всё меньше и меньше устаревших соединений, в поддержании которых нет необходимости.
В то время как эти улучшения касались, в основном, вышеописанных пограничных случаев, связанных с разработкой новых возможностей, с ними оказался связан и один приятный побочный эффект. Он заключался в дальнейшем повышении надёжности доставки сообщений. Наша система уже тогда отличалась хорошей надёжностью доставки, а её дополнительное улучшение привело к тому, что показатель, характеризующий успешность доставки сообщений, регулярно выражался числом с пятью девятками.
Свежие разработки
У нас имеется прочное основание системы и огромное количество соединений. Что теперь с этим всем можно сделать? Этот вопрос был и остаётся той движущей силой, которая лежит в основе почти всего свежего функционала, построенного на базе Pushy. И это — восхитительный вопрос, особенно — когда задаёшь его в команде, которая занимается инфраструктурой.
Сдвиг в сторону прямой отправки сообщений
Первое изменение, которое коснулось традиционной модели поведения Pushy, заключается во внедрении схемы работы, которую мы называем «прямой отправкой сообщений» (direct push). Бэкенд‑сервис, вместо того, чтобы помещать сообщения в асинхронную очередь, может воспользоваться библиотекой Push и совсем эту очередь не использовать. Когда библиотеку Push просят напрямую доставить сообщение, она ищет экземпляр Pushy, подключённый к целевому устройству, просматривая Push‑реестр, а потом отправляет сообщение прямо этому экземпляру Pushy. А он пришлёт в ответ код состояния, говорящий либо о том, что он смог успешно доставить сообщение, либо о том, что столкнулся с ошибкой. Библиотека Pushy, в свою очередь, доведёт эти сведения до кода сервиса, инициировавшего отправку сообщения.
Автор Pushy добавил в систему этот функционал в качестве необязательного дополнительного механизма, но за годы работы почти все бэкенд‑сервисы пользовались прямой доставкой сообщений. Модель её функционирования, когда система «делает всё возможное» для доставки сообщения, оказалась для них достаточно хорошей. В последние годы, по мере того, как растут потребности бэкенд‑сервисов, мы наблюдаем за тем, что объём использования прямой отправки сообщений прямо‑таки «взлетает». В частности, вместо того, чтобы просто организовать работу по схеме «сделать всё возможное», эти прямые сообщения дают вызывающему сервису мгновенную обратную связь от системы доставки сообщений, позволяя сервисам повторять отправку сообщений в том случае, если целевое устройство на момент отправки сообщения было недоступно.
В наши дни прямой обмен сообщениями отвечает за обработку основного объёма сообщений, проходящих через Pushy. Например, в предыдущем 24-часовом периоде на прямые сообщения пришлась нагрузка, в среднем, оцениваемая в 160 000 сообщений в секунду, а на обычные — около 50 000.
Обмен сообщениями между устройствами
По мере того, как мы размышляли над этим новым подходом к работе с сообщениями, эволюционировали и наши представления о том, кто может являться отправителем сообщения. Что если мы хотим отойти от изначальной схемы работы Pushy, когда сервис отвечает за доставку сообщений с сервера на устройства пользователей? Что если устройства сами смогут отправлять сообщения бэкенд‑сервисам или даже друг другу? Наша система сообщений традиционно была однонаправленной: мы отправляли сообщения с серверов на устройства. А теперь мы воспользуемся идеями двунаправленных соединений и прямой доставки сообщений для реализации того, что мы называем «обменом сообщениями между устройствами». Эта система лежала в основе ранних примеров взаимодействия телефонов и телевизоров, поддерживая игры вроде Triviaverse. Она же является основой режима, называемого Companion Mode, когда телефон и телевизор постоянно обмениваются сообщениями.
Всё это требует более глубокого, чем ранее, понимания системы. Ведь нам нужна информация не только об отдельном устройстве, а более обширные сведения. Например — сведения о том, какие устройства, с которыми может взаимодействовать телефон, подключены к учётной записи пользователя. Тут возникают и такие задачи, как, например, подписка на события устройства, позволяющая узнать о том, когда устройство выходит в сеть, и когда к нему можно подключиться, или когда можно отправить на него сообщение. Эта система была построена с применением дополнительного сервиса (Device List Service), которые получает от Pushy сведения о подключённых устройствах. Соответствующие события, отправляемые посредством топика Kafka, дают сервису сведения о том, какие устройства подключены к конкретной учётной записи. Устройства могут подписываться на эти события, что позволяет им получать сообщения от сервиса в том случае, когда другие устройства, подключённые к той же учётной записи, выходят в сеть.
Список устройств позволяет решить вопрос обнаружения устройств при отправке сообщений между ними. После того, как устройство, подключённое к некоей учётной записи, узнает о других устройствах, подключённых к той же учётной записи — оно может выбрать из списка целевое устройство и отправить ему сообщение.
А именно — после того, как в распоряжении устройства есть список других устройств, подключённых к той же учётной записи, что и оно, это устройство может отправить сообщение экземпляру Pushy, к которому подключено, воспользовавшись его WebSocket‑соединением с этим устройством. В этом сообщении будут указаны сведения о целевом устройстве (1 на схеме, расположенной ниже). Pushy ищет метаданные целевого устройства в Push‑реестре (2) и отправляет сообщение ещё одному экземпляру Pushy, к которому подключено целевое устройство (3), поступая так, как если бы это был бэкенд‑сервис, что мы обсуждали выше, рассматривая прямые сообщения. Второй экземпляр Pushy доставляет сообщение целевому устройству (4), а первый Pushy получает в ответ код состояния, который он может передать устройству, инициировавшему связь (5).
Протокол обмена сообщениями
Мы разработали простой протокол, основанный на JSON, предназначенный для обмена сообщениями между устройствами. Он позволяет оформлять и пересылать сообщения, предназначенные для передачи между устройствами. Мы, как команда, занимающаяся сетевыми вопросами, совершенно естественным образом склоняемся в сторону абстрагирования коммуникационного уровня системы, применяя везде, где это возможно, инкапсуляцию. Применение обобщённого подхода к оформлению сообщений означает, что команды, отвечающие за работу с устройствами, могут, на основе нашего протокола, определять собственные протоколы. А Pushy будет играть роль транспортного уровня системы, передавая сообщения туда, куда нужно.
Универсальность протокола обмена сообщениями приносит свои плоды в смысле экономии вложений в разработку и поддержки функционирующих систем. Мы создали основной объём функционала протокола в октябре 2022 года, а после этого нам понадобилось лишь внести в него небольшие доработки. Нам почти ничего не пришлось в нём менять. Дело в том, что команды, работающие над клиентским ПО, разрабатывали свои решения на основе нашего протокола. Они создавали протоколы более высокого уровня, ориентированные на конкретные устройства и обеспечивающие работу тех функций, над которыми работали эти команды. Нам очень и очень нравится работать с командами‑партнёрами. Но у нас есть возможность сделать так, чтобы они, не привлекая к работе нас, могли бы самостоятельно создавать что‑то своё на базе нашей инфраструктуры. А это значит, что мы смогли ускорить их работу и облегчить им жизнь, смогли не выходить за рамки своей роли по обеспечению других команд инструментами для передачи сообщений.
Сейчас первые разработки, основанные на нашем протоколе передачи сообщений, находятся в экспериментальной фазе. Каждую секунду в Pushy фиксируется, в среднем, около 1000 сообщений, передаваемых между устройствам. Это число, со временем, будет только расти.
Детали реализации
В Pushy за обработку входящих WebSocket‑сообщений отвечает класс PushClientProtocolHandler
(входит в состав пакета com.netflix.zuul.netty.server.push), расширяющий класс Netty ChannelInboundHandlerAdapter
и добавляемый в конвейер Netty для каждого соединения с клиентом. Мы ожидаем поступления WebSocket‑сообщений от подключённого устройства в методе этого класса channelRead
и парсим входящие сообщения. Если это — сообщение, которое предназначено для передачи от устройства к устройству — мы передаём объекту DeviceToDeviceManager
сообщение, а так же — объекты ChannelHandlerContext
и PushUserAuth
(идентификационные сведения пользователя).
Объект DeviceToDeviceManager
отвечает за валидацию сообщения, решает кое‑какие служебные задачи и выполняет асинхронный вызов для проверки того, является ли целевое устройство авторизованным получателем сообщения. Далее — он обращается к Pushy для поиска целевого устройства в локальном кеше (или делает запрос к хранилищу данных в том случае, если найти устройство в кеше не удаётся). После этого он перенаправляет сообщение. Этот код выполняется в асинхронном режиме для предотвращения блокировки цикла событий из‑за этих вызовов. Объект DeviceToDeviceManager
, кроме того, отвечает за наблюдаемость системы, формируя метрики, характеризующие попадания кеша, обращения к хранилищу данных, скорость доставки сообщений, перцентильные показатели задержек. Мы серьёзно наблюдали за этими метриками, получая уведомления о каких‑то внештатных ситуациях и применяя их для оптимизации системы. Ведь Pushy — это, на самом деле, сервис для работы с метриками, который время от времени доставляет одно‑два сообщения!
Безопасность
Когда речь идёт о работе в пограничных областях облака Netflix, особое внимание всегда уделяется соображениям безопасности. Для каждого HTTPS‑подключения мы сделали так, что сообщения можно передавать только по аутентифицированным WebSocket‑подключениям. Мы ограничили скорость передачи сообщений и добавили проверки, обеспечивающие то, что некое устройство может отправлять данные только на те устройства, с которым ему разрешено связываться. Уверен, ваши намерения чисты, но мне очень не хотелось бы, чтобы вы могли бы слать какие угодно данные со своего телевизора на мой (вы, уверен, тоже не хотели бы, чтобы кто‑то что‑то слал на ваш телевизор!).
Задержки передачи данных и другие соображения
Одним из важнейших вопросов, которые надо учитывать при создании проектов на основе Pushy, является вопрос задержек передачи данных. Особенно — когда передача сообщений используется для интерактивной работы с приложением Netflix.
Мы добавили в Pushy кеширование для того чтобы сократить количество операций поиска данных, вероятность частого изменения которых невелика. Это могут быть списки целевых устройств, которым может отправлять сообщение некое устройство, это может быть экземпляр Pushy, к которому подключено некое устройство. Нам, при обработке первых сообщений, нужно провести кое‑какие операции поиска, чтобы узнать о том, куда их отправлять. Но кеширование позволяет нам доставлять следующие сообщения быстрее и без поиска данных в хранилище KeyValue. При выполнении тех запросов, где, благодаря кешированию, обращение к KeyValue не производится, нам удаётся решать наши задачи гораздо быстрее. Благодаря нашим усилиям медианное время, проходящее с момента поступления входящего сообщения в Pushy и до момента отправки устройству ответа, сократилось до значения, меньшего, чем миллисекунда. При этом 99 перцентиль задержек передачи данных представлен числом, меньшим 4 мс.
Задержки передачи данных при обращении к хранилищу KeyValue обычно очень низки, но мы сталкивались с короткими периодами повышенных задержек чтения данных из‑за собственных проблем хранилища KeyValue. Общий показатель задержек рос и в других частях Pushy, например — в тех, что отвечают за регистрацию клиентов. При этом применение механизма кеширования привело к тому, что мы замечаем лишь небольшой рост задержек при обмене сообщениями между устройствами.
Культурные аспекты, позволяющие делать то, что мы делаем
Масштаб Pushy и соображения, лежащие в основе его архитектуры, сделали работу над этим сервером задачей, интересной с технической точки зрения. Но мы, кроме того, сознательно обращаем внимание и на нетехнические аспекты, которые помогли Pushy расти и развиваться. Так, мы уделяем особое внимание итеративной разработке, когда в первую очередь решают самые сложные задачи. При этом проекты часто начинаются с чего‑то вроде прототипа, который позволяет быстро оценить полезность некоей идеи. Когда мы создаём такую вот первоначальную версию проекта, мы изо всех сил стараемся учитывать нужды будущего, что позволяет нам быстро переходить от поддержки некоего узконаправленного продукта к созданию более общего, универсального решения. Например, в случае с нашей системой обмена сообщениями между устройствами, мы смогли решить сложные задачи на ранних этапах работы над Triviaverse. А потом то, что мы сделали, доросло до универсального решения для обмена данными между устройствами.
Как хорошо видно на схеме системы, приведённой выше, Pushy существует не в вакууме. Его окружают проекты, для реализации которых часто нужны усилия как минимум полудюжины команд. Доверие, опыт, общение, хорошие взаимоотношения — благодаря всему этому и был создан Pushy. Наша команда не существовала бы, если не существовали бы пользователи нашей платформы, и мы, определённо, не писали бы этот пост, если бы не вся та работа, которую проделали продуктовая и клиентская команды. Всё это, кроме прочего, указывает на важность таких понятий, как «создавать» и «делиться с другими». Если мы способны создать прототип проекта вместе с командой, занимающейся устройствами — это значит что мы можем провести демонстрацию этого проекта и услышать идеи других команд. Одно дело — рассказать о том, что можно отправлять какие‑то там сообщения, и совсем другое — показать телевизор, который быстро реагирует на касание кнопки контроллера, открытого на телефоне!
Будущее Pushy
Если и есть в этом мире что‑то, в чём можно быть уверенным, так это то, что Pushy продолжит расти и развиваться. Сейчас у нас в работе много новых возможностей. Среди них — проксирование WebSocket‑сообщений, отслеживание сообщений, глобальная широковещательная рассылка сообщений, функционал подписок для поддержки Games и Live. С учётом всего того, что вложено в Pushy, он представляет собой стабильную, прочную базу, готовую поддержать новое поколение возможностей Netflix.
О, а приходите к нам работать? 🤗 💰
Мы в wunderfund.io занимаемся высокочастотной алготорговлей с 2014 года. Высокочастотная торговля — это непрерывное соревнование лучших программистов и математиков всего мира. Присоединившись к нам, вы станете частью этой увлекательной схватки.
Мы предлагаем интересные и сложные задачи по анализу данных и low latency разработке для увлеченных исследователей и программистов. Гибкий график и никакой бюрократии, решения быстро принимаются и воплощаются в жизнь.
Сейчас мы ищем плюсовиков, питонистов, дата-инженеров и мл-рисерчеров.
ссылка на оригинал статьи https://habr.com/ru/articles/861996/
Добавить комментарий