Обмен сообщениями в режиме реального времени: опыт Slack

от автора

А вы знали, что земные станции передают сигнал спутникам, расположенным на геостационарной орбите на высоте 35 786 метров над экватором, и что ответные сигналы накрывают целое полушарие? Сегодня спутниковые радиостанции обслуживают сотни каналов. Если вы только не работаете на секретном военном объекте или глубоко под землёй, то спутниковый радиосигнал к вашим услугам найдётся практически везде.

Платформа Slack подобна спутникам в том, что на ней ежедневно рассылаются миллионы сообщений по миллионам каналов — всё это в режиме реального времени. Если рассмотреть трафик типичного рабочего дня, оказывается, что большинство пользователей остаются онлайн с 9.00 по 17.00 по местному времени, причём, пиковые нагрузки приходятся на период с 11.00 до 14.00, с небольшим спадом в районе обеденного перерыва. Хотя, в разных регионах рабочее время распределено примерно схоже, на следующем графике наблюдаются два пика. Очевидно, что «час пик» совпадает не везде. В некоторых регионах он приходится на послеполуденные часы, в других наступает до полудня. Цветные линии на следующей диаграмме обозначают разные регионы.

image

В этой статье мы расскажем о той архитектуре, которая позволяет нам рассылать сообщения в режиме реального времени и в таком масштабе. Мы подробнее рассмотрим сервисы, обслуживающие чаты с сообщениями и реагирующие на различные события, происходящие со стороны пользователя при работе в режиме реального времени. Сервисы Slack написаны на Java. Ниже мы поговорим о канальных серверах (CS), серверах шлюзов (GS), серверах администрирования (AS) и серверах присутствия (PS).

Обзор серверов

Канальные серверы (CS) сохраняют состояние и работают в оперативной памяти, и на них в некотором объёме хранится история работы в каналах. Каждый CS соответствует подмножеству каналов, подбираемому на основе согласованного хеширования. В моменты пиковых нагрузок каждый хост обслуживает примерно по 16 миллионов каналов. «Канал» в данном случае — это абстрактная сущность, ID которой может быть присвоен, например, пользователю, команде, предприятию, файлу, созвону или обычному каналу Slack. ID канала хешируется и отображается на уникальный сервер. Каждый CS-хост получает и отправляет сообщения от отображаемых на него каналов. Отдельная команда Slack работает со всеми своими каналами, отображаемыми на все CS.

Менеджеры согласованных кольцевых хешей (CHARM) управляют согласованным кольцевым хешем для каждого CS. Они очень быстро и эффективно заменяют неисправные CS. Новый CS можно подготовить к обслуживанию трафика менее чем за 20 секунд. Тогда как каналы одной команды распределены по всем CS, лишь небольшое количество каналов команды отображается на CS. При замене канального сервера пользователи из тех команд, что привязаны к этому серверу, испытывают повышенную задержку при доставке сообщений, и этот период длится менее 20 секунд.

На следующей схеме показано, как канальные серверы регистрируются в Consul — инструменте обнаружения серверов. Каждый из согласованных хешей определяется и управляется своим CHARM, после чего серверы администрирования (AS) и CS обнаруживают их, запрашивая у Consul актуальную конфигурацию.

image

Серверы шлюзов (GS) сохраняют состояние и работают в оперативной памяти. На них хранится информация о пользователях и подписки на каналы веб-сокетов. Этот сервис служит интерфейсом между клиентами Slack и канальными серверами. Серверы шлюзов, в отличие от всех остальных категорий, развёртываются сразу во множестве географических регионов. Благодаря этому клиент Slack может быстро подключиться к GS-хосту в ближайшем регионе. В Slack предусмотрен механизм «стока», купирующий региональные отказы и бесшовно переключающий пользователей из сбоящего региона на сервера из ближайшего исправного региона…

Серверы администрирования (AS) не сохраняют состояния и работают в оперативной памяти. Они служат интерфейсом между бэкендом веб-приложения и канальными серверами. Серверы присутствия (PS) работают в оперативной памяти. С их помощью отслеживается, какие пользователи сейчас онлайн. Именно благодаря этим серверам рядом с аватаром доступного пользователя горит знакомый зелёный кружочек. Пользователи хешируются на отдельно взятых серверах присутствия. Slack-клиенты направляют к ним запросы через веб-сокет, используя при этом GS в качестве прокси-сервера, своевременно показывающего статус присутствия и изменения этого статуса. Slack-клиент получает уведомления о присутствии, касающиеся только того подмножества пользователей, которые видны на экране приложения в конкретный момент.

Настройка клиента Slack

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

image

При загрузке клиент выбирает с бэкенда веб-приложения пользовательский токен и информацию об установке соединения через веб-сокет. Веб-приложение — это база кода Hacklang, в которой хранятся все API, вызываемые клиентами Slack. Также в этом сервисе есть код на JavaScript, отвечающий за отображение клиентов Slack. Клиент инициирует соединение через веб-сокет и направляет его в ближайший периферийный регион. Сервер Envoy переадресует запрос на GS. Envoy — это свободный периферийный прокси-сервер, рассчитанный на работу с исходно облачными приложениями. Envoy используется в Slack в качестве балансировщика нагрузки, распределяемой по сервисам, а также обеспечивает завершение TLS. Сервер шлюза выбирает информацию о пользователя, при этом учитываются все каналы данного пользователя. Сервер получает эту информацию от веб-приложения, которое отправляет первое сообщение клиенту. После этого GS подписывается на все канальные серверы, на которых содержатся релевантные каналы. Эта работа выполняется по принципу согласованного асинхронного хеширования. Всё, теперь клиент Slack готов отправлять и получать сообщения в режиме реального времени.

Отправка сообщения миллиону клиентов в режиме реального времени

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

image

Рассмотрим, как сообщение широковещательно передаётся всем клиентам, которые сейчас находятся онлайн. После того, как веб-сокет настроен в соответствии с описанной выше процедурой, клиент обращается к API веб-приложения, чтобы тот отправил сообщение. После этого веб-приложение отправляет сообщение на сервер администрирования. Сервер администрирования проверяет, какой ID канала указан в этом сообщении, обнаруживает канальный сервер через согласованный кольцевой хеш и перенаправляет сообщение именно на тот канальный сервер, на котором обслуживается обмен сообщениями в режиме реального времени в этом канале. Сообщение рассылается на все серверы шлюзов во всем мире, подписанные на этот канал. Каждый сервер шлюза, который получит это сообщение, пересылает это сообщение каждому из подключённых к нему клиентов, которые подписаны на каналы с данным ID.

Ниже показано, какой путь проделывает сообщение от клиента через весь стек. В следующем примере клиенты Slack A и B находятся в одном периферийном регионе, а C — в другом регионе. Клиент A отправляет сообщение, а клиенты B и C его получают.

image

События

Наряду с сообщениями из чатов есть ещё одна специфическая разновидность сообщений — так называемые «события». Событие — это любое обновление, которое клиент получает в режиме реального времени, и которое меняет состояние этого клиента. Насчитывается много сотен типов событий, проходящих через серверы Slack. В качестве примеров можно привести реакцию пользователя на сообщение, добавление закладки или присоединение нового пользователя к каналу. Эти события проделывают примерно такой же путь, как показан выше для простого сообщения в чате.

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

image

Теперь рассмотрим иную разновидность событий, которые называются «кратковременными» (transient). Это события иного рода, такие, которые не сохраняются в базе данных. При передаче они проходят немного иной путь. Примеры таких событий — сообщение о том, что пользователь набирает в канале текст или передаёт документ.

image

Ниже схематически представлен этот сценарий. Вновь клиенты Slack A и B находятся в одном и том же периферийном регионе, а C — в другом регионе. Клиент A набирает в канале текст, и уведомление об этом поступает пользователям B и C, также находящимся в этом канале. Клиент A отправляет это сообщение через веб-сокет на сервер шлюза. Сервер шлюза проверяет в сообщении, каков ID канала — и перенаправляет сообщение на нужный канальный сервер, который узнаёт из согласованного кольцевого хеша. После этого канальный сервер посылает сообщение всем серверам шлюзов во всём мире, подписанным на этот канал. Каждый сервер шлюза, получив это сообщение, широковещательно передаёт его на веб-сокеты всех пользователей, подписанных на этот канал.

image

Напоследок

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

P.S. Обращаем ваше внимание на то, что у нас на сайте проходит распродажа.


ссылка на оригинал статьи https://habr.com/ru/articles/894986/