36a7f79c86
- Added support for launching applications without a graphical window using `-NOSPLASH` and `-NOWINDOW` flags. - Improved process termination logic with a multi-step approach: soft termination, forced termination, and PowerShell fallback. - Updated README to reflect new features and enhancements in process management.
413 lines
21 KiB
Markdown
413 lines
21 KiB
Markdown
# Session Server
|
||
|
||
Сессионный сервер для автоматической регистрации на основном API сервере.
|
||
|
||
## Описание
|
||
|
||
Session Server - это приложение, которое автоматически регистрируется на основном сервере (`stream.graff.tech`) и периодически обновляет свою информацию. Это позволяет основному серверу знать о доступных сессионных серверах для распределения нагрузки.
|
||
|
||
## Возможности
|
||
|
||
- 🚀 Автоматическая регистрация при запуске
|
||
- 🔄 Периодическое обновление информации о сервере (по умолчанию каждые 30 секунд после завершения предыдущего запроса)
|
||
- 🎮 Автоматическое определение и обновление свободной GPU памяти через nvidia-smi (по умолчанию каждую секунду после завершения предыдущего запроса)
|
||
- 🎯 Автоматическое управление игровыми сессиями
|
||
- Запуск приложений для сессий со статусом "starting"
|
||
- Остановка приложений для сессий со статусом "ending"
|
||
- Отслеживание PID запущенных процессов
|
||
- Автоматическое обновление статусов сессий на главном сервере
|
||
- 🌐 Автоматическое определение локального IP адреса
|
||
- 💻 Автоматическое определение hostname
|
||
- ⚙️ Гибкая конфигурация через переменные окружения
|
||
- 🔁 Автоматические повторные попытки при ошибках
|
||
- 📝 Подробное логирование
|
||
- ⚡ Защита от наложения запросов для всех операций (рекурсивный вызов через setTimeout)
|
||
|
||
## Установка
|
||
|
||
```bash
|
||
# Установка зависимостей
|
||
bun install
|
||
```
|
||
|
||
## Конфигурация
|
||
|
||
Создайте файл `.env` в корне проекта на основе `.env.example`:
|
||
|
||
```bash
|
||
# URL основного API сервера
|
||
API_URL=http://localhost:3000
|
||
|
||
# Тип сервера: stream или local
|
||
SERVER_TYPE=stream
|
||
|
||
# Расположение сервера (ОБЯЗАТЕЛЬНО для stream серверов)
|
||
# Возможные значения: ru1, uae1
|
||
SERVER_LOCATION=ru1
|
||
|
||
# Уровень сервера (только для stream серверов)
|
||
# Возможные значения: demo, prod
|
||
SERVER_TIER=demo
|
||
|
||
# ID филиала (ОБЯЗАТЕЛЬНО для local серверов)
|
||
BRANCH_ID=00000000-0000-0000-0000-000000000000
|
||
|
||
# Локальный IP адрес (опционально, определяется автоматически)
|
||
# LOCAL_IP=192.168.1.100
|
||
|
||
# Hostname сервера (опционально, определяется автоматически)
|
||
# HOSTNAME=my-session-server
|
||
|
||
# Интервал регистрации в миллисекундах (по умолчанию 30000 = 30 секунд)
|
||
REGISTER_INTERVAL_MS=30000
|
||
|
||
# Интервал обновления GPU памяти в миллисекундах (по умолчанию 1000 = 1 секунда)
|
||
GPU_UPDATE_INTERVAL_MS=1000
|
||
|
||
# Интервал проверки сессий в миллисекундах (по умолчанию 5000 = 5 секунд)
|
||
SESSION_CHECK_INTERVAL_MS=5000
|
||
```
|
||
|
||
### Переменные окружения
|
||
|
||
| Переменная | Описание | Обязательна | По умолчанию |
|
||
|------------|----------|-------------|--------------|
|
||
| `API_URL` | URL основного API сервера | Нет | `http://localhost:3000` |
|
||
| `SERVER_TYPE` | Тип сервера (`stream` или `local`) | Нет | `stream` |
|
||
| `SERVER_LOCATION` | Расположение (`ru1`, `uae1`) - **обязательно для `stream`** | Да (для stream) | - |
|
||
| `SERVER_TIER` | Уровень (`demo`, `prod`) - только для `stream` | Нет | `demo` (для stream) |
|
||
| `BRANCH_ID` | ID филиала - **обязательно для `local`** | Да (для local) | - |
|
||
| `LOCAL_IP` | Локальный IP адрес | Нет | Определяется автоматически |
|
||
| `HOSTNAME` | Hostname сервера | Нет | Определяется автоматически |
|
||
| `REGISTER_INTERVAL_MS` | Интервал регистрации в мс | Нет | `30000` |
|
||
| `GPU_UPDATE_INTERVAL_MS` | Интервал обновления GPU памяти в мс | Нет | `1000` |
|
||
| `SESSION_CHECK_INTERVAL_MS` | Интервал проверки сессий в мс | Нет | `5000` |
|
||
|
||
**Примечание:** Свободная память GPU (`gpuFreeMb`) автоматически определяется через `nvidia-smi` при каждой регистрации и обновляется каждую секунду (или согласно `GPU_UPDATE_INTERVAL_MS`) после завершения предыдущего запроса. Это предотвращает наложение запросов. Если `nvidia-smi` недоступен, сервер завершит работу с ошибкой.
|
||
|
||
## Управление сессиями
|
||
|
||
Session Server автоматически управляет игровыми сессиями на этом сервере:
|
||
|
||
### Как это работает
|
||
|
||
1. **Проверка сессий**: Каждую секунду (или согласно `SESSION_CHECK_INTERVAL_MS`) сервер запрашивает у основного API список сессий для этого сервера
|
||
2. **Проверка времени окончания**: Для активных сессий (`started` или `starting`):
|
||
- ⏰ Проверяется время `endAt` - если оно наступило, сессия автоматически переводится в статус `ending`
|
||
- **Timezone**: Все время хранится в UTC, сравнение корректно работает независимо от часового пояса сервера
|
||
- По умолчанию сессии создаются с временем окончания +30 минут от момента создания
|
||
- Автоматическое завершение гарантирует, что сессии не будут работать бесконечно
|
||
3. **Запуск приложений**: Для сессий со статусом `starting`:
|
||
- ⏰ Проверяется время `startAt` - приложение запускается только если это время уже наступило
|
||
- **Timezone**: Все время хранится в UTC, сравнение корректно работает независимо от часового пояса сервера
|
||
- Запланированные сессии (с будущим `startAt`) логируются с информацией о времени до запуска
|
||
- 🎯 **Централизованное назначение сервера** (для stream-сессий):
|
||
- **Main server** автоматически назначает серверы каждые 5 секунд
|
||
- Выбирается сервер с максимальной свободной GPU памятью
|
||
- Учитываются требования приложения к GPU памяти (`gpuLimitMb`)
|
||
- Session-server просто проверяет, что сессия назначена ему
|
||
- Запускается соответствующее приложение
|
||
- Отслеживается PID процесса
|
||
- Статус сессии обновляется на `started` на главном сервере
|
||
4. **Остановка приложений**: Для сессий со статусом `ending`:
|
||
- Используется `taskkill /pid {PID} /T /F` для завершения всего дерева процессов
|
||
- `/T` - завершает указанный процесс и ВСЕ дочерние процессы
|
||
- `/F` - принудительное завершение
|
||
- Решает проблему с UE5 и другими приложениями, создающими дочерние процессы
|
||
- Статус сессии обновляется на `ended` на главном сервере
|
||
5. **Автоматическая очистка**: Процессы для неактивных сессий автоматически останавливаются
|
||
|
||
### API endpoints для управления сессиями
|
||
|
||
Session Server взаимодействует с основным сервером через следующие endpoints:
|
||
|
||
- `GET /servers/:id/sessions` - получить список сессий для сервера
|
||
- `PATCH /sessions/:id/status` - обновить статус сессии (публичный endpoint)
|
||
|
||
### Запуск приложений
|
||
|
||
Session Server автоматически запускает `.exe` приложения по стандартному пути:
|
||
|
||
```
|
||
C:\apps\{appName}\{appName}.exe
|
||
```
|
||
|
||
Где `{appName}` - это значение поля `name` из таблицы `apps`.
|
||
|
||
#### Структура директорий
|
||
|
||
Все приложения должны быть размещены в следующей структуре:
|
||
```
|
||
C:\apps\
|
||
├── minecraft\
|
||
│ └── minecraft.exe
|
||
├── fortnite\
|
||
│ └── fortnite.exe
|
||
└── cyberpunk\
|
||
└── cyberpunk.exe
|
||
```
|
||
|
||
#### Особенности запуска и остановки
|
||
|
||
- ✅ Автоматическая проверка существования exe файла
|
||
- ✅ Рабочая директория устанавливается в папку приложения (`C:\apps\{appName}\`)
|
||
- ✅ Окно консоли скрывается (`windowsHide: true`)
|
||
- ✅ **Запуск без графического окна** - использует флаги `-NOSPLASH` и `-NOWINDOW` для UE приложений
|
||
- ✅ PID процесса отслеживается и передается на main server
|
||
- ✅ Автоматическое обновление статуса при завершении процесса
|
||
- ✅ **Многоступенчатое завершение процессов**:
|
||
1. Мягкое завершение (`taskkill /T`) - отправляет WM_CLOSE сообщение окнам
|
||
2. Принудительное завершение (`taskkill /T /F`) - если мягкое не сработало
|
||
3. PowerShell завершение - закрывает окна и убивает процесс через PowerShell
|
||
- ✅ Решает проблему с UE5 приложениями, которые создают множественные процессы и имеют графические окна
|
||
|
||
#### Логи запуска
|
||
|
||
```
|
||
[2025-10-06T10:00:00.000Z] 🚀 Запуск приложения "minecraft" для сессии abc-123
|
||
[2025-10-06T10:00:00.050Z] 📂 Путь к приложению: C:\apps\minecraft\minecraft.exe
|
||
[2025-10-06T10:00:01.000Z] ✅ Приложение запущено с PID 12345
|
||
[2025-10-06T10:00:01.100Z] ✅ Статус сессии abc-123 обновлен на "started"
|
||
```
|
||
|
||
#### Обработка ошибок
|
||
|
||
Если exe файл не найден:
|
||
```
|
||
❌ Файл приложения не найден: C:\apps\minecraft\minecraft.exe. Убедитесь, что приложение установлено.
|
||
```
|
||
|
||
### Примеры логов
|
||
|
||
Запланированная сессия:
|
||
```
|
||
[2025-10-06T10:00:00.000Z] ⏰ Сессия 123e4567-e89b-12d3-a456-426614174000 (minecraft) запланирована через 120 сек
|
||
```
|
||
|
||
Запуск сессии:
|
||
```
|
||
[2025-10-06T10:02:00.000Z] 🚀 Запуск приложения "minecraft" для сессии 123e4567-e89b-12d3-a456-426614174000
|
||
[2025-10-06T10:02:01.000Z] ✅ Приложение запущено с PID 12345
|
||
[2025-10-06T10:02:01.100Z] ✅ Статус сессии 123e4567-e89b-12d3-a456-426614174000 обновлен на "started"
|
||
```
|
||
|
||
Автоматическое завершение по истечении времени:
|
||
```
|
||
[2025-10-06T10:32:00.000Z] ⏰ Время сессии 123e4567-e89b-12d3-a456-426614174000 (minecraft) истекло, завершение...
|
||
[2025-10-06T10:32:00.100Z] ✅ Статус сессии 123e4567-e89b-12d3-a456-426614174000 обновлен на "ending"
|
||
```
|
||
|
||
Остановка сессии:
|
||
```
|
||
[2025-10-06T10:32:00.000Z] 🛑 Остановка приложения для сессии 123e4567-e89b-12d3-a456-426614174000 (PID: 12345)
|
||
[2025-10-06T10:32:00.500Z] ✅ Дерево процессов для PID 12345 успешно завершено
|
||
[2025-10-06T10:32:00.600Z] ✅ Приложение и все дочерние процессы остановлены для сессии 123e4567-e89b-12d3-a456-426614174000
|
||
[2025-10-06T10:32:00.700Z] ✅ Статус сессии 123e4567-e89b-12d3-a456-426614174000 обновлен на "ended"
|
||
```
|
||
|
||
## Отладка проблем (Troubleshooting)
|
||
|
||
Если приложение запускается и сразу завершается с ошибкой, см. подробное руководство по диагностике:
|
||
- **[TROUBLESHOOTING.md](./TROUBLESHOOTING.md)**
|
||
|
||
### Новые возможности логирования
|
||
|
||
После обновления вы увидите:
|
||
- 🔴 **STDERR логи** для диагностики ошибок приложений
|
||
- 📊 **Счётчик активных процессов** при запуске/завершении
|
||
- ⚠️ **Детальная информация** о кодах выхода и сигналах
|
||
|
||
Пример логов с ошибкой:
|
||
```
|
||
[10:51:56.498Z] 🚀 Запуск приложения "ShishimGorka" для сессии abc (активных процессов: 1)
|
||
[10:51:56.563Z] ✅ Приложение запущено с PID 84112 (всего активных: 2)
|
||
[10:51:57.500Z] 🔴 STDERR [abc]: Error: Port 8888 is already in use
|
||
[10:51:57.894Z] 🛑 Приложение для сессии abc завершилось с кодом 1
|
||
[10:51:57.894Z] ⚠️ Приложение завершилось с ошибкой! Код выхода: 1
|
||
```
|
||
|
||
## Работа с часовыми поясами (Timezone)
|
||
|
||
Session Server корректно работает с разными часовыми поясами:
|
||
|
||
### Как это работает
|
||
|
||
1. **База данных**: PostgreSQL хранит все timestamp с timezone в UTC
|
||
2. **API**: Основной сервер возвращает время в формате ISO 8601 с timezone (например, `2025-10-06T10:00:00.000Z`)
|
||
3. **Session Server**: JavaScript автоматически парсит ISO 8601 строки в UTC
|
||
4. **Сравнение**: Все сравнения времени происходят в UTC, независимо от локального часового пояса сервера
|
||
|
||
### Миграция базы данных
|
||
|
||
⚠️ **Важно**: Если вы обновляете существующую систему, необходимо выполнить миграцию для добавления timezone в поля `startAt` и `endAt`:
|
||
|
||
```bash
|
||
# Из директории server/
|
||
psql -d $DATABASE_URL -f timezone_migration.sql
|
||
```
|
||
|
||
См. `server/TIMEZONE_MIGRATION.md` для подробностей.
|
||
|
||
### Примеры
|
||
|
||
Если сервер в России создаёт сессию на 14:00 по московскому времени (UTC+3):
|
||
- В БД сохранится: `2025-10-06T11:00:00.000Z` (UTC)
|
||
- Сервер в ОАЭ (UTC+4) увидит: 15:00 по местному времени
|
||
- Session Server запустит приложение ровно в 11:00 UTC, независимо от локального времени
|
||
|
||
## Запуск
|
||
|
||
### Development режим
|
||
|
||
```bash
|
||
bun run dev
|
||
```
|
||
|
||
### Production режим
|
||
|
||
```bash
|
||
# Собрать проект
|
||
bun run build
|
||
|
||
# Запустить собранное приложение
|
||
bun run start
|
||
```
|
||
|
||
## Примеры использования
|
||
|
||
### Stream сервер в России (демо)
|
||
|
||
```env
|
||
API_URL=https://api.stream.graff.tech
|
||
SERVER_TYPE=stream
|
||
SERVER_LOCATION=ru1
|
||
SERVER_TIER=demo
|
||
REGISTER_INTERVAL_MS=30000
|
||
```
|
||
|
||
### Stream сервер в ОАЭ (продакшн)
|
||
|
||
```env
|
||
API_URL=https://api.stream.graff.tech
|
||
SERVER_TYPE=stream
|
||
SERVER_LOCATION=uae1
|
||
SERVER_TIER=prod
|
||
REGISTER_INTERVAL_MS=30000
|
||
```
|
||
|
||
### Local сервер для филиала
|
||
|
||
```env
|
||
API_URL=https://api.stream.graff.tech
|
||
SERVER_TYPE=local
|
||
BRANCH_ID=123e4567-e89b-12d3-a456-426614174000
|
||
REGISTER_INTERVAL_MS=60000
|
||
```
|
||
|
||
## Логирование
|
||
|
||
Сервер выводит подробную информацию о своей работе:
|
||
|
||
```text
|
||
============================================================
|
||
🚀 Запуск сессионного сервера
|
||
============================================================
|
||
Конфигурация:
|
||
API URL: http://localhost:3000
|
||
Hostname: DESKTOP-ABC123
|
||
Local IP: 192.168.1.100
|
||
Type: stream
|
||
GPU Free MB: 8192 (читается из nvidia-smi)
|
||
Location: ru1
|
||
Tier: demo
|
||
Register Interval: 30000ms
|
||
GPU Update Interval: 1000ms
|
||
============================================================
|
||
[2025-10-06T12:00:00.000Z] Регистрация сервера...
|
||
Данные: {
|
||
"localIp": "192.168.1.100",
|
||
"hostname": "DESKTOP-ABC123",
|
||
"type": "stream",
|
||
"gpuFreeMb": 8192,
|
||
"location": "ru1",
|
||
"tier": "demo"
|
||
}
|
||
[2025-10-06T12:00:00.123Z] ✅ Сервер успешно зарегистрирован
|
||
ID сервера: 123e4567-e89b-12d3-a456-426614174000
|
||
[2025-10-06T12:00:01.000Z] 🎮 GPU память обновлена: 8150 MB
|
||
[2025-10-06T12:00:02.000Z] 🎮 GPU память обновлена: 8120 MB
|
||
[2025-10-06T12:00:03.000Z] 🎮 GPU память обновлена: 8100 MB
|
||
```
|
||
|
||
## Обработка ошибок
|
||
|
||
Сервер автоматически обрабатывает ошибки:
|
||
|
||
- **Валидация конфигурации**:
|
||
- Stream-серверы: обязательно должен быть указан `SERVER_LOCATION`
|
||
- Local-серверы: обязательно должен быть указан `BRANCH_ID`
|
||
- При отсутствии обязательных полей сервер завершит работу с критической ошибкой
|
||
- **Сетевые ошибки**: Автоматические повторные попытки (до 3 раз)
|
||
- **Таймауты**: 10 секунд на запрос
|
||
- **Коды ошибок**: Повторная отправка при временных ошибках сервера (408, 429, 500, 502, 503, 504)
|
||
- **Ошибки GPU**: Если `nvidia-smi` недоступен или возвращает некорректные данные, сервер завершит работу с критической ошибкой
|
||
- **Критические ошибки**: Подробное логирование с завершением работы
|
||
|
||
## Архитектура
|
||
|
||
```text
|
||
┌──────────────────────────────────┐
|
||
│ Session Server │
|
||
│ │
|
||
│ Регистрация (рекурсивно): │
|
||
│ 1. Определение IP │
|
||
│ 2. Определение hostname │
|
||
│ 3. Запрос GPU (nvidia-smi) │
|
||
│ 4. POST /servers/register │
|
||
│ 5. setTimeout → повтор (30s) │
|
||
│ │
|
||
│ Обновление GPU (рекурсивно): │
|
||
│ 1. Запрос GPU (nvidia-smi) │
|
||
│ 2. PATCH /servers/:id/gpu │
|
||
│ 3. setTimeout → повтор (1s) │
|
||
└──────────┬───────────────────────┘
|
||
│
|
||
│ HTTP Requests
|
||
│
|
||
▼
|
||
┌──────────────────────────────────┐
|
||
│ Main API Server │
|
||
│ │
|
||
│ 1. Проверка по hostname │
|
||
│ 2. Создание/обновление │
|
||
│ 3. Обновление GPU памяти │
|
||
│ 4. Возврат данных │
|
||
└──────────────────────────────────┘
|
||
```
|
||
|
||
## Разработка
|
||
|
||
### Структура проекта
|
||
|
||
```text
|
||
session-server/
|
||
├── src/
|
||
│ └── index.ts # Основной файл приложения
|
||
├── dist/ # Собранное приложение
|
||
├── package.json
|
||
├── tsconfig.json
|
||
├── bun.build.ts # Конфигурация сборки
|
||
└── README.md
|
||
```
|
||
|
||
### Требования
|
||
|
||
- Bun >= 1.0
|
||
- Node.js >= 18 (опционально)
|
||
- TypeScript >= 5.0
|
||
- **NVIDIA GPU с установленным `nvidia-smi`** (обязательно)
|
||
|
||
## Лицензия
|
||
|
||
MIT
|