🇬🇧 English | 🇺🇦 Українська
1. Вступ до NATS¶
Цей розділ — не про плагін. Він про сам NATS: що це за система, якими словами вона розмовляє і навіщо в ній два різні режими — Core і JetStream. Якщо ви вже знайомі з NATS, сміливо переходьте одразу до 2. Швидкого старту. Якщо ні — прочитайте цей розділ повністю: усі наступні розділи спираються на терміни, введені тут.
Що таке NATS¶
NATS — це брокер повідомлень: окрема програма (сервер), яка приймає повідомлення від одних клієнтів і роздає їх іншим. Ваша гра — теж клієнт, який підключається до цього сервера звичайним TCP-з'єднанням.
Навіщо взагалі окремий сервер, якщо актори в Unreal і так можуть викликати функції одне в одного? Бо NATS вирішує три задачі, які на прямих викликах не працюють:
- Учасники не повинні знати одне про одного. Сервер гри публікує подію
game.match.finished— і йому байдуже, чи є зараз хоч один підписник. Аналітика, логер і система досягнень підпишуться на цю подію незалежно одне від одного, кожен своїм екземпляром гри чи окремим сервісом. - Учасники можуть бути в різних процесах і на різних машинах. Гравець на одному комп'ютері, dedicated server — на іншому, сервіс аналітики — третій процес. NATS-сервер — єдина точка, до якої підключається кожен.
Сам плагін нічого не реплікує по мережі Unreal — див. попередження в FAQ → «Чи плагін реплікує щось мережею Unreal?». - Публікатор не чекає на підписників.
Publishповертає керування одразу — сервер сам розносить копію повідомлення всім, хто підписаний, паралельно, без черги запит-відповідь на кожного.
Subject: адреса повідомлення¶
Усе в NATS адресується subject — рядком з токенів, розділених крапкою:
game.events.player.join, chat.lobby.42, orders.created. Це не файлова система й не URL
— просто ієрархічна адреса без наперед визначеної структури: скільки токенів і що вони
означають, вирішуєте ви самі в межах свого проєкту.
Публікатор завжди вказує точний subject. Підписник може вказати точний subject або шаблон із двома спеціальними символами:
| Символ | Значення | Приклад | Підходить | Не підходить |
|---|---|---|---|---|
* |
Рівно один токен | game.events.* |
game.events.join |
game.events.player.join |
> |
Один або більше токенів, до кінця | game.events.> |
game.events.join, game.events.player.join |
game.other |
> має сенс лише останнім токеном шаблону. Приклад для цілої гри:
game.events.player.join ← конкретна подія
game.events.player.* ← усі одиночні події гравця (join, leave, death — не damage.taken)
game.events.> ← геть усе під game.events, будь-якої глибини
Один і той самий підписник може одночасно отримувати повідомлення від кількох публікаторів на різних subject — досить підписатися на спільний шаблон.
Publish/Subscribe: базовий патерн¶
Це основа всього NATS. Публікатор надсилає повідомлення на subject; кожен, хто зараз підписаний на цей (або відповідний шаблону) subject, отримує свою копію.
Publisher NATS-сервер Subscriber A (subject: chat.>)
│ │ │
├─ Publish "chat.lobby.1" ────►│ │
│ ├──────── копія ───────────────►│ отримав
│ │
│ │ Subscriber B (subject: chat.lobby.2)
│ │ │
│ │ (не отримав — інший subject)
Найважливіший наслідок цієї моделі: якщо на subject немає жодного підписника в момент публікації, повідомлення просто зникає. NATS Core нічого не зберігає й нікому нічого не винен — це «fire-and-forget» у буквальному сенсі. Підписник, що приєднався на секунду пізніше, вже нічого не отримає. Якщо вам потрібно, щоб повідомлення дочекалося підписника, який ще не підключився, — це причина, з якої існує JetStream (нижче).
Request/Reply: коли потрібна відповідь¶
Publish/Subscribe — однобічний потік. Коли потрібна саме відповідь на конкретний запит («дай дані гравця за ID»), NATS не додає новий примітив — той самий Publish/Subscribe використовується хитріше:
- Запитувач створює тимчасовий subject-«скриньку» (inbox), унікальний для цього запиту.
- Запитувач підписується на цю скриньку.
- Запитувач публікує запит на цільовий subject, вказуючи скриньку як
reply-to. - Відповідач, отримавши повідомлення, бачить
reply-toі публікує відповідь саме туди. - Запитувач отримує відповідь на своїй скриньці — або спливає тайм-аут, якщо ніхто не відповів.
Requester NATS Responder
│ │ │
├─ Subscribe "_INBOX.abc123" ───►│ │
├─ Publish "svc.users.get" │ │
│ reply-to="_INBOX.abc123" ────►│──────── доставлено ────────────►│
│ │ │ обробив запит
│ │◄─── Publish "_INBOX.abc123" ────┤
│◄──────── доставлено ───────────┤ │
│ відповідь отримана │ │
Плагін ховає всю цю механіку за одним викликом — Request Async — детальніше в
5. Основний обмін повідомленнями.
Заголовки: метадані поруч з даними¶
Окрім самих даних, повідомлення може нести заголовки — пари ключ-значення, схожі на заголовки HTTP: тип вмісту, ідентифікатор трасування, версія схеми. Дані й заголовки передаються окремо, тому парсити заголовки з тіла повідомлення не треба.
Core проти JetStream: два режими одного сервера¶
Усе вище — це NATS Core: найпростіший, найшвидший режим, без збереження на диск чи в пам'ять понад миттєву доставку. Той самий сервер, за бажання, вмикає й другий режим — JetStream: шар персистентності поверх Core.
| Core | JetStream | |
|---|---|---|
| Що зберігається | Нічого. Немає підписника — повідомлення втрачено | Повідомлення потрапляють у стрім (Stream) і лишаються там |
| Хто отримує | Лише ті, хто підписаний у момент публікації | Будь-хто, хто створить споживача (Consumer) — навіть через годину після публікації |
| Підтвердження доставки | Немає | Споживач підтверджує кожне повідомлення (ACK); без підтвердження — повторна доставка |
| Швидкість | Найвища | Трохи повільніша — сервер записує повідомлення, перш ніж підтвердити публікацію |
| Типове застосування | Живі події: рух гравця, чат у реальному часі, стан лобі | Усе, що не можна втратити: замовлення, досягнення, журнал подій, чергу завдань |
Важливо розуміти: JetStream не замінює Core, а додається до нього. Subject
orders.created можна публікувати і без жодного стріму — просто ніхто його не збереже.
Щойно ви створюєте стрім, налаштований на цей subject, сервер починає його зберігати —
клієнтський код публікації при цьому не змінюється.
Стрім (Stream): де зберігаються повідомлення¶
Стрім — це іменоване сховище повідомлень, схоже на таблицю в базі даних. Ви створюєте стрім один раз, вказавши:
- ім'я — наприклад
ORDERS; - subject(и), які він захоплює — наприклад
orders.>. Усе, що публікується на такий subject, автоматично потрапляє в стрім, доки він існує; - скільки зберігати — за кількістю повідомлень, за розміром чи за часом;
- де зберігати — у пам'яті (швидко, зникає при перезапуску сервера) чи на диску (переживає перезапуск).
Публікатор нічого не знає про стріми — він просто публікує на subject. Сервер сам вирішує, чи є стрім, що захоплює цей subject, і якщо є — зберігає копію.
Споживач (Consumer): курсор читання зі стріму¶
Стрім зберігає повідомлення, але сам їх нікому не надсилає — для читання потрібен споживач, курсор із власною позицією в стрімі. Кілька споживачів можуть незалежно читати той самий стрім, кожен зі своєї позиції.
Є два режими доставки:
- Push-споживач — сервер сам надсилає повідомлення вам, щойно вони з'являються. Схоже на звичайну підписку Core, тільки з підтвердженням і гарантією, що нічого не загубиться.
- Pull-споживач — ви самі просите: «дай мені до 10 повідомлень». Зручно, коли швидкість обробки має контролювати саме споживач — наприклад, черга завдань, яку сервер обробляє пакетами.
Після обробки кожного повідомлення споживач має його підтвердити (ACK). Без підтвердження протягом заданого часу сервер вважає, що повідомлення не оброблено, і надішле його ще раз.
Key-Value: сховище поверх стріму¶
Key-Value (KV) — надбудова над JetStream, яка виглядає як звичайний словник «ключ →
значення»: Put("player_42", "..."), потім Get("player_42"). Під капотом це той самий
стрім, де кожен запис — окреме повідомлення на службовому subject виду
$KV.<бакет>.<ключ>, а «остання версія ключа» — просто останнє повідомлення з таким
subject. Звідси й корисні побічні ефекти:
- Ревізії. Кожен запис ключа отримує номер версії, що зростає — можна перевіряти, чи ключ не змінили паралельно (оптимістична конкурентність).
- Watch. Можна підписатися на зміни ключа чи групи ключів у реальному часі — це те саме Core-підписування на службовий subject, просто плагін ховає деталі.
- Історія. Якщо бакет налаштовано зберігати кілька версій, можна прочитати попередні значення ключа.
Що далі¶
Тепер, коли термінологія на місці, розділ 2. Швидкий старт проведе вас через перше підключення, першу публікацію й перший стрім — уже конкретними нодами й кодом цього плагіна.
Далі: 2. Швидкий старт