Раздел 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-сервера?» Образец: «Все его соединения рвутся, и клиенты переподключаются к другим узлам — для остальных пользователей система продолжает работать, единой точки полного отказа нет. Чтобы массовый реконнект не завалил соседей, использую плавный вывод узла, разброс и нарастающую паузу в переподключении. Пропущенные сообщения клиент догружает из очереди, а идемпотентность не даёт им задвоиться.»
#мессенджер #балансировка #маршрутизация #реестр-сессий #деградация #переподключения #подготовка-к-интервью