Цю сторінку ще не перекладено — показано англійську версію.
Під капотом: транспорт і хаб
Це архітектурний довідник, а не інтерфейс, який ви викликаєте. Нічого з цієї сторінки немає в API, з яким в и працюєте: сокетами, каналами, конвертами й повторними спробами керуєте не ви. Шари стека названо на сторінці Основні поняття, а сам механізм описано тільки тут — щоб архітектор міг перевірити, що робить SDK з обірваним з’єднанням, вимкненим модулем або повідомленням, яке має дійти рівно один раз.
Стек шарів
П’ять шарів згори вниз: простір користувача, модулі, примітиви, хаб і під ним транспортні адаптери. Два верхні шари належать користувачеві, а все, що нижче, — внутрішня справа SDK.
- Простір користувача — це ваш код. Він бачить модулі, і на цьому його словник закінчується.
- Модулі — прикладний шар: кімнати, підбір гравців, інвентар, таблиці лідерів. Вони утворюють граф, а не дерево, і саме цей граф хаб має розв’язати, коли один із модулів вимикають (саму цю структуру описано на сторінці Успадкування і композиція).
- Примітиви — перша реалізація, на яку посилається все інше: дані, події, RPC, групи. Модуль — це іменована збірка примітивів плюс його власні правила.
- Хаб — це контролер: впровадження залежностей, монтування модулів, сесія користувача, відновлення стану, якість обслуговування повідомлень і маршрутизація кожного вхідного повідом лення до модуля, змонтованого для нього.
- Транспорти — адаптери до протоколу. Їх кілька, і хаб поводиться з усіма однаково.
Транспорти — це адаптери
Транспортів буде більше одного, і відрізняються вони так, що без адаптерів ці відмінності просочилися б у кожен модуль:
| Вимір | Варіанти |
|---|---|
| Форма | на повідомленнях або на запитах |
| Канали | один канал або кілька |
| Стан | з відновленням стану з’єднання або без нього |
| Протокол | TCP або UDP |
Сьогодні це WebSocket, UDP-транспорт PlayServ і звичайний HTTP. Кожен із них — адаптер, за яким сховано деталі його реалізації, і кожен надає нагору одне й те саме: транспортну сесію. Хаб тримає сесію, а не сокет, тож ніщо вище адаптера не має справи з мережевим інтерфейсом.
Одна транспортна сесія за раз
Одночас но працюють один транспортний канал і одна транспортна сесія. Окремий бекенд-транспорт для таблиць лідерів (leaderboard), поки живу сесію несе транспорт майстер-клієнта, поки не підтримується, і жодна сигнатура API не залежить від того, чи відкрито кілька каналів. Доки пізніша версія не скаже інакше, контрактом є одна сесія.
Хаб повністю приховує транспорт
Донизу хаб говорить мовою транспортного інтерфейсу. Нагору він надає стан, події та сесію користувача. Ні код модуля, ні код гри не можуть дізнатися, який транспорт під ними, як хаб зібрав виклики в пакет або що він зробив, щоб повернутися до узгодженого стану після розриву.
- Сесія користувача належить хабу, а не модулю. Перепідключення, продовження сесії та відновлення стану хаб виконує один раз — для всього, що на ньому змонтовано.
- QoS повідомлень належить хабу, а не модулю даних. Конверти, повторні спроби й пакування — це механіка хабу.
QoS повідомлень: три рівні
Модуль оголошує лише ту гарантію доставки, яка йому потрібна:
| Рівень | Що означає |
|---|---|
at least once | повідомлення доставляють повторно, доки не прийде підтвердження; отримувач має витримувати дублікати |
at most once | повідомлення надсилають один раз і ніколи не повторюють; втрата допустима |
exactly once | з дедуплікацією й підтвердженням; найдорожчий рівень, його використовують там, де він справді потрібен |
Як виконати гарантію, вирішує хаб — не модуль і не ви.
DI, монтування і вимкнені модулі
Хаб створює екземпляри модулів і монтує їх — у корінь або в простір імен, — розв’язуючи залежності кожного модуля від примітивів та інших модулів. Якщо збірці модуль не потрібен, хаб його не монтує. Монтування прив’язане до простору імен, і якщо другий модуль претендує на вже зайняту точку монтування (mount point), хаб відхиляє його ще під час монтування: композиція ламається саме там, а не на першому виклику.
Оскільки модулі утворюють граф, вимкнення одного з них має наслідки далі з а ланцюжком, і хаб обирає рівно один із двох шляхів:
- Вимкнути залежний ланцюжок. Кожен модуль, якому потрібен відсутній, теж вимикається, і його інтерфейсів просто немає — вони не падають з помилкою.
- Оголосити обмежену функціональність. Залежні модулі лишаються змонтованими й повідомляють, чого вже не можуть робити.
Третього шляху немає: змонтований модуль ніколи мовчки не відкидає операції, яких уже не може виконати, тож вимкнену залежність завжди видно.
Що лишається над лінією
Усі обіцянки зі сторінок модулів справджуються над лінією: зміна сутності (entity) — це мережева операція, хук — типізована функція, вхід у кімнату — один виклик. Назви шарів стека можуть вам траплятися — Основні поняття посилаються сюди, — але нічого під лінією не є викликом, який робить ваш код. Ці шари існують для того, щоб обіцянки над ними пережили зміну транспорту.
З чим ви таки зіткнетеся — контекст доставки, у якому працюють ваші обробники, момент, коли завершується дескриптор (handle), і реалізація в пам’яті, з якою ви тестуєте, — описано на рівень вище: Потоки, час життя і тести.