Перейти до основного вмісту
За межами SDK

Цю сторінку ще не перекладено — показано англійську версію.

Під капотом: транспорт і хаб

Це архітектурний довідник, а не інтерфейс, який ви викликаєте. Нічого з цієї сторінки немає в API, з яким ви працюєте: сокетами, каналами, конвертами й повторними спробами керуєте не ви. Шари стека названо на сторінці Основні поняття, а сам механізм описано тільки тут — щоб архітектор міг перевірити, що робить SDK з обірваним з’єднанням, вимкненим модулем або повідомленням, яке має дійти рівно один раз.

Стек шарів

user spacethe SDK's own businessyour codemodules: rooms, matchmaking, inventory, leaderboards…primitives: events · RPC · data · groupsthe hub: DI and mounting · the user session · state recovery · message QoS · routingtransport adapters: WebSocket · PlayServ UDP · HTTP
Стек шарів: над лінією — ваш код і модулі, під нею — примітиви, хаб і транспорти

П’ять шарів згори вниз: простір користувача, модулі, примітиви, хаб і під ним транспортні адаптери. Два верхні шари належать користувачеві, а все, що нижче, — внутрішня справа SDK.

  • Простір користувача — це ваш код. Він бачить модулі, і на цьому його словник закінчується.
  • Модулі — прикладний шар: кімнати, підбір гравців, інвентар, таблиці лідерів. Вони утворюють граф, а не дерево, і саме цей граф хаб має розв’язати, коли один із модулів вимикають (саму цю структуру описано на сторінці Успадкування і композиція).
  • Примітиви — перша реалізація, на яку посилається все інше: дані, події, RPC, групи. Модуль — це іменована збірка примітивів плюс його власні правила.
  • Хаб — це контролер: впровадження залежностей, монтування модулів, сесія користувача, відновлення стану, якість обслуговування повідомлень і маршрутизація кожного вхідного повідомлення до модуля, змонтованого для нього.
  • Транспорти — адаптери до протоколу. Їх кілька, і хаб поводиться з усіма однаково.

Транспорти — це адаптери

Транспортів буде більше одного, і відрізняються вони так, що без адаптерів ці відмінності просочилися б у кожен модуль:

ВимірВаріанти
Формана повідомленнях або на запитах
Каналиодин канал або кілька
Станз відновленням стану з’єднання або без нього
ПротоколTCP або UDP

Сьогодні це WebSocket, UDP-транспорт PlayServ і звичайний HTTP. Кожен із них — адаптер, за яким сховано деталі його реалізації, і кожен надає нагору одне й те саме: транспортну сесію. Хаб тримає сесію, а не сокет, тож ніщо вище адаптера не має справи з мережевим інтерфейсом.

Одна транспортна сесія за раз

Одночасно працюють один транспортний канал і одна транспортна сесія. Окремий бекенд-транспорт для таблиць лідерів (leaderboard), поки живу сесію несе транспорт майстер-клієнта, поки не підтримується, і жодна сигнатура API не залежить від того, чи відкрито кілька каналів. Доки пізніша версія не скаже інакше, контрактом є одна сесія.

Хаб повністю приховує транспорт

Донизу хаб говорить мовою транспортного інтерфейсу. Нагору він надає стан, події та сесію користувача. Ні код модуля, ні код гри не можуть дізнатися, який транспорт під ними, як хаб зібрав виклики в пакет або що він зробив, щоб повернутися до узгодженого стану після розриву.

  • Сесія користувача належить хабу, а не модулю. Перепідключення, продовження сесії та відновлення стану хаб виконує один раз — для всього, що на ньому змонтовано.
  • QoS повідомлень належить хабу, а не модулю даних. Конверти, повторні спроби й пакування — це механіка хабу.

QoS повідомлень: три рівні

Модуль оголошує лише ту гарантію доставки, яка йому потрібна:

РівеньЩо означає
at least onceповідомлення доставляють повторно, доки не прийде підтвердження; отримувач має витримувати дублікати
at most onceповідомлення надсилають один раз і ніколи не повторюють; втрата допустима
exactly onceз дедуплікацією й підтвердженням; найдорожчий рівень, його використовують там, де він справді потрібен

Як виконати гарантію, вирішує хаб — не модуль і не ви.

DI, монтування і вимкнені модулі

Хаб створює екземпляри модулів і монтує їх — у корінь або в простір імен, — розв’язуючи залежності кожного модуля від примітивів та інших модулів. Якщо збірці модуль не потрібен, хаб його не монтує. Монтування прив’язане до простору імен, і якщо другий модуль претендує на вже зайняту точку монтування (mount point), хаб відхиляє його ще під час монтування: композиція ламається саме там, а не на першому виклику.

Оскільки модулі утворюють граф, вимкнення одного з них має наслідки далі за ланцюжком, і хаб обирає рівно один із двох шляхів:

  1. Вимкнути залежний ланцюжок. Кожен модуль, якому потрібен відсутній, теж вимикається, і його інтерфейсів просто немає — вони не падають з помилкою.
  2. Оголосити обмежену функціональність. Залежні модулі лишаються змонтованими й повідомляють, чого вже не можуть робити.

Третього шляху немає: змонтований модуль ніколи мовчки не відкидає операції, яких уже не може виконати, тож вимкнену залежність завжди видно.

Що лишається над лінією

Усі обіцянки зі сторінок модулів справджуються над лінією: зміна сутності (entity) — це мережева операція, хук — типізована функція, вхід у кімнату — один виклик. Назви шарів стека можуть вам траплятися — Основні поняття посилаються сюди, — але нічого під лінією не є викликом, який робить ваш код. Ці шари існують для того, щоб обіцянки над ними пережили зміну транспорту.

З чим ви таки зіткнетеся — контекст доставки, у якому працюють ваші обробники, момент, коли завершується дескриптор (handle), і реалізація в пам’яті, з якою ви тестуєте, — описано на рівень вище: Потоки, час життя і тести.