Курс «Архитектура мессенджера» · Часть 4 · Масштаб: группы, каналы, регионы

Раздел 12. Балансировка, шлюзы и маршрутизация соединений

Как раскидывать миллионы соединений; балансировщики и connection-серверы; реестр сессий (кто где подключён); деградация и переподключения.

В Разделе 3 мы разобрали детали по отдельности: зачем постоянное соединение, что делают серверы соединений, что такое Реестр сессий. Здесь смотрим на ту же систему с двух новых сторон: как она выдерживает миллионы соединений и как не падает целиком, когда что-то ломается. Это операционный, «боевой» взгляд.

Как раскидывают миллионы соединений

Балансировка идёт не в один слой, а в несколько — соединения дробят постепенно:

  • На самом входе клиентов распределяют ещё до балансировщиков — например, на уровне адресов и точек входа разные пользователи направляются в разные дата-центры или группы балансировщиков. Трафик дробится ещё «на подлёте».
  • Балансировщики раскидывают соединения по серверам соединений. Работают они (как мы помним из Раздела 3) на транспортном уровне и держат каждое соединение «прилипшим» к своему узлу на всё время жизни.
  • Серверы соединений добавляют и убирают под нагрузку: вырос трафик — подняли новые узлы, спал — погасили.

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

Маршрутизация: найти получателя на другом узле

Это центральный механизм. Алиса на узле A, Боб на узле Б — и узел A сам по себе не знает про соединение Боба. Решают двумя инструментами, которые работают вместе.

Реестр сессий — справочник адресов. Это быстрое общее хранилище (живёт в памяти, типа Redis), где записано «пользователь/устройство → на каком узле он сейчас висит». При отправке Message-сервис смотрит в реестр, узнаёт узел получателя и пересылает сообщение туда по внутренней сети.

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

Шина публикаций-подписок (pub/sub) — для рассылки многим. Когда получателей много (группа, канал), искать в реестре каждого по отдельности невыгодно. Тут удобнее шина: узлы подписываются на «каналы» своих пользователей, а отправитель публикует сообщение один раз — и все нужные узлы сами его получают. Pub/sub буквально и значит «публикация и подписка».

Итого: реестр — для точечной адресации одному, шина — для рассылки многим. На практике их сочетают.

Деградация: ломаться по чуть-чуть, а не сразу всё

Под перегрузкой система не должна падать целиком — она должна корректно деградировать (по-английски graceful degradation): жертвовать менее важным, сохраняя главное.

Принцип — приоритеты. Доставка сообщений важнее статусов присутствия. Поэтому при перегрузке система сначала перестаёт обновлять «печатает…» и онлайн-статусы (вспомни Раздел 7 — их и так не жалко), а сообщения продолжает доставлять. Лучше отключить второстепенное, чем уронить главное.

Сюда же — обратное давление (backpressure): если следующий узел не успевает обрабатывать поток, предыдущий притормаживает приём или ставит в очередь, а не заваливает соседа и не падает сам.

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

Переподключения: норма, а не авария

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

Опасный момент — шторм переподключений (из Раздела 3): когда узел упал или его выкатывают, его соединения рвутся разом и клиенты ломятся заново все сразу. Защита:

  • плавный вывод узла из работы (drain) при выкатке — не рубить соединения резко;
  • разброс задержки переподключения у клиентов (jitter) плюс нарастающая пауза (backoff), чтобы они не пришли в одну секунду;
  • расчёт на то, что данные не теряются: пропущенное лежит в очереди.

Что делает клиент, когда переподключился: заново регистрируется в реестре, переподписывается на свои каналы и догружает пропущенное из очереди, опираясь на номер «докуда я уже всё получил» (Разделы 5, 6, 10). А идемпотентность (Раздел 6) гарантирует, что повторы при переподключении не задвоятся.

Вопросы интервьюера (с разбором)

«Как сервер А отправит сообщение получателю, висящему на сервере Б?» Образец: «Через Реестр сессий. Узел A не знает чужих соединений напрямую, поэтому Message-сервис смотрит в реестр, где записано, на каком узле сейчас получатель, и пересылает сообщение на узел Б по внутренней сети — а тот уже проталкивает его клиенту. Для групп вместо поиска каждого использую шину публикаций-подписок: публикую один раз, нужные узлы получают сами.»

«Как балансируешь постоянные соединения?» Образец: «В несколько слоёв: сначала клиентов дроблю ещё на входе по точкам входа и дата-центрам, потом балансировщик на транспортном уровне раскидывает соединения по узлам, и каждое соединение прилипает к своему узлу на всё время жизни. Узлы автоматически добавляю и убираю под нагрузку. Цель — ровный разброс без горячих узлов.»

«Что происходит при падении connection-сервера?» Образец: «Все его соединения рвутся, и клиенты переподключаются к другим узлам — для остальных пользователей система продолжает работать, единой точки полного отказа нет. Чтобы массовый реконнект не завалил соседей, использую плавный вывод узла, разброс и нарастающую паузу в переподключении. Пропущенные сообщения клиент догружает из очереди, а идемпотентность не даёт им задвоиться.»


#мессенджер #балансировка #маршрутизация #реестр-сессий #деградация #переподключения #подготовка-к-интервью