Эта страница ещё не переведена — показана английская версия.
Под капотом: транспорт и хаб
Это справка по архитектуре, а не интерфейс, который вы вызываете. Ничего из описанного здесь нет в API, с которым вы работаете: сокетами, каналами, конвертами и повторными отправками вы не управляете. Слои стека перечислены на странице Основные понятия, а как они работают, описано только здесь. Так архитектор может проверить, что делает SDK при обрыве соединения, с выключенным модулем или с сообщением, которое должно дойти ровно один раз.
Стек слоёв
Слоёв пять, сверху вниз: пользовательское пространство, модули, примитивы, хаб и под ним транспортные адаптеры. Два верхних слоя принадлежат пользователю, а всё, что ниже, — внутреннее дело SDK.
- Пользовательское пространство — это ваш код. Он видит модули, и на них его словарь заканчивается.
- Модули — прикладной слой: комнаты, подбор игроков, инвентарь, таблицы лидеров. Они образуют граф, а не дерево, и именно этот граф приходится разрешать хабу, когда один из модулей выключен (сама эта структура описана на странице Наследование и композиция).
- Примитивы — первая реализация, на которую опирается всё остальное: данные, события, RPC, группы. Модуль — это именованная сборка примитивов плюс его собственные правила.
- Хаб — это контроллер. Он отвечает за внедрение зависимостей, монтирование модулей, пользовательскую сессию, восстановление состояния, качество доставки сообщений и передаёт каждое входящее сообщение модулю, смонтированному для него.
- Транспорты — адаптеры к протоколам. Их несколько, и хаб обращается со всеми одинаково.
Транспорты — это адаптеры
Транспортов будет несколько, и различаются они так, что без адаптеров эти различия просочились бы в каждый модуль:
| Параметр | Варианты |
|---|---|
| Модель обмена | на сообщениях или на запросах |
| Каналы | один канал или несколько |
| Состояние | с восстановлением состояния соединения или без него |
| Протокол | TCP или UDP |
Сегодня это WebSocket, UDP-транспорт PlayServ и обычный HTTP. Каждый из них — транспортный адаптер (transport adapter), за которым скрыты детали его реализации, и каждый отдаёт наверх одно и то же — транспортную сессию. Хаб держит сессию, а не сокет, поэтому выше ада птера о сетевом интерфейсе никто не думает.
Одна транспортная сессия за раз
Одновременно открыты один транспортный канал и одна транспортная сессия. Держать отдельный бэкенд-транспорт для таблиц лидеров, пока транспорт мастер-клиента несёт живую сессию, SDK не поддерживает, и ни одна сигнатура API не рассчитывает на несколько открытых каналов. Пока в следующих версиях не решат иначе, контракт — одна сессия.
Хаб полностью скрывает транспорт
Вниз хаб общается через интерфейс транспорта, а наверх отдаёт состояние, события и пользовательскую сессию. Ни код модулей, ни код игры не может узнать, какой транспорт под ним, как хаб собрал вызов в пакет и что он сделал, чтобы после разрыва вернуться в согласованное состояние.
- Пользовательская сессия принадлежит хабу, а не модулю. Переподключение, возобновление и восстановление состояния происходят один раз, в хабе, сразу для всего, что в нём смонтировано.
- QoS сообщений принадлежит хабу, а не модулю данных. Конверты, повторные отправки и упаковка — это механика хаба.
QoS сообщений: три уровня
Модуль объявляет только ту гарантию доставки, которая ему нужна:
| Уровень | Что означает |
|---|---|
at least once | доставляется повторно, пока не придёт подтверждение, поэтому получатель должен справляться с дубликатами |
at most once | отправляется один раз и не повторяется — потеря допустима |
exactly once | с дедупликацией и подтверждением — самый дорогой уровень, его берут только там, где он действительно нужен |
Как выполнить гарантию, решает хаб — не модуль и не вы.
DI, монтирование и выключенные модули
Хаб создаёт модули и монтирует их — в корень или в пространство имён, — разрешая зависимости каждого модуля от примитивов и от других модулей. Если модуль сборке не нужен, она его не монтирует. Монтирование идёт по пространствам имён. Если второй модуль претендует на уже занятую точку монтирования (mount point), он пол учает отказ прямо при монтировании: композиция ломается в этот момент, а не при первом обращении к ней.
Модули образуют граф, поэтому выключение одного из них сказывается на тех, кто от него зависит. Хаб выбирает ровно один из двух путей:
- Выключить зависимую цепочку. Каждый модуль, которому нужен отсутствующий, тоже выключается, и его интерфейсов просто нет — они не падают с ошибкой.
- Объявить урезанную функциональность. Зависимые модули остаются смонтированными и сообщают, чего больше не умеют.
Третьего пути нет. Смонтированный модуль никогда молча не пропускает операции (operation), которые больше не может выполнить, поэтому выключенную зависимость всегда видно.
Что остаётся над чертой
Все обещания со страниц модулей выполняются над чертой: изменить сущность (entity) — значит отправить по сети, хук — это типизированная функция, вход в комнату — один вызов. Названия слоёв стека вам могут встретиться — страница Основные понятия ссылается сюда, — но ничего ниже черты ваш код не вызывает. Эт и слои нужны, чтобы обещания наверху пережили смену транспорта.
А вот с чем вы столкнётесь — в каком контексте доставки выполняются ваши обработчики, когда заканчивается жизнь дескриптора и с какой реализацией в памяти запускать тесты, — описано уровнем выше, на странице Потоки, время жизни и тесты.