Сервер считает ход игрока за 5–10 миллисекунд. Пока он занят одним, остальные ждут своей очереди. При сотне игроков последний получит ответ почти через секунду — для него игра превратится в слайд-шоу. Эта часть о том, как обслуживать всех одновременно и чем за это платит железо.

Очередь, которая растёт

Хорошим пингом в играх считается 60 миллисекунд. В этот бюджет входит всё: дорога пакета до сервера, само вычисление и дорога обратно. Последовательная обработка съедает бюджет уже на десятке игроков.

Последовательная обработка: время ответа растёт вместе с числом игроков. Параллельная — нет.
Последовательная обработка: время ответа растёт вместе с числом игроков. Параллельная — нет.

Выход один — считать параллельно. Тогда время ответа перестаёт зависеть от числа игроков онлайн.

Два режима PHP

PHP работает в двух режимах, и у каждого своя роль в игровом сервере.

FPM — режим коротких процессов. Управляющий процесс поднимает дочерние по мере запросов, каждый отвечает и умирает. Между запросами он ничего не помнит.

CLI — режим долгоживущего процесса. Вопреки названию, командная строка на экране для него не нужна: скрипт работает демоном в фоне. Именно здесь живут WebSocket-сервер, который держит игроков, и NPC, которым нужно ходить и атаковать своей жизнью.

FPM отвечает на запрос и умирает. CLI живёт в фоне и держит состояние мира в памяти.
FPM отвечает на запрос и умирает. CLI живёт в фоне и держит состояние мира в памяти.

Режимы вызывают друг друга. Из FPM можно запустить CLI-процесс функциями семейства exec — это стоит около 30 миллисекунд. Из CLI можно постучаться прямо в сокет FPM и поднять процесс там, эмулируя обычный запрос.

Чем за это платят

Процессорное время. Даже ничего не делающий CLI-процесс занимает около 0,7% процессора одного ядра. Сотня NPC, разведённых по отдельным процессам, съедает машину целиком.

Память. PHP резервирует под процесс примерно на 50% больше, чем тот фактически использует, — это видно через memory_get_usage. Плюс при каждом запуске в память заново грузятся все файлы фреймворка. Умножьте на сотню процессов.

Кеш врозь. Библиотеки вроде APCu кешируют объекты и массивы прямо в памяти без сериализации, но кеш FPM и кеш CLI не видят друг друга: в FPM он живёт до перезапуска, в CLI — до конца работы демона и только внутри него.

Обойтись одним режимом тоже нельзя. Поднимать CLI-процесс на каждый запрос игрока — это те самые 30 миллисекунд, половина всего бюджета пинга.

Что с этим делать

Opcache. Фреймворк компилируется и кладётся в общую память один раз, вместо загрузки в каждый процесс заново. На моём сервере это дало трёхкратное сокращение расхода памяти.

Пулы. Вместо процесса на каждого NPC — один процесс на карту, который синхронно ведёт всех её обитателей. Процессор тратит те же 0,7% на пул из сотни NPC, что тратил бы на одного бездельничающего демона.

Сто отдельных процессов против одного пула: та же работа за 0,7% процессора вместо 70%.
Сто отдельных процессов против одного пула: та же работа за 0,7% процессора вместо 70%.

Общая память. Раз APCu между режимами не работает, обмен данными между демонами приходится строить на других инструментах — например, на shmop и подобных расширениях для разделяемой памяти.

Что было дальше

Пулы и разделяемая память закрыли первый рубеж, но открытый мир на несколько физических серверов потребовал другой архитектуры. Как это устроено сейчас — в части про открытый бесшовный мир, она в списке ниже.

А какой предел одновременных игроков вы упирались на своём стеке и что оказалось узким местом — процессор, память или сеть?

История:

  1. Введение

  2. Масштабируемость и асинхронность

  3. WebSocket

  4. Redis

  5. LUA и JavaScript

  6. Выбор технологий, протокола и архитектурный шаблон Entity Component System

  7. Игровые локации (тайловые карты)

  8. Клиентская часть на Unity

  9. Игровые серверные механики

  10. Открытый бесшовный мир в 2D игре

  11. FPS, Ping, паузы между командами, интерполяция и экстраполяция

  12. Очереди и параллельное программирование на CPU

  13. Event-driven паттерн, JSON-RPC и почему не сервисная (SOA) архитектура

  14. Сетевая карта и задержка кадра (Latency frame) по RFC 2544 (1242)

  15. Создание сервера для онлайн ММО игр на PHP

  16. Готовое MVP сервиса 2D MMO RPG игр (realtime)

  17. Внедряю ИИ: механики из одного описания

  18. Конвейер контента для MMO: импорт анимации, экипировка и обновление игры без патчей

  19. Каталог ассетов и git: как командой вести игру в облаке

  20. Тесты для кода, который пишет ИИ: контракты вместо ассертов

Only registered users can participate in poll. Log in, please.
Стала ли статья лучше по качеству / восприятие чем первая часть?
44.44%Все так же плохо и ничего не получится12
29.63%Стало чуть лучше, но я не верю что выйдет задуманный сервис8
22.22%Лучше или хуже, но сама идея такого сервиса кажется реальной и полезной6
3.7%Стало лучше1
27 users voted. 12 users abstained.