Ленивая Обработка Команд В Социальной Игре

Всем привет! Сегодня я хочу поделиться с вами решением проблемы, с которой мы столкнулись при разработке социальной игры.

Клиент игры был написан на Flash, а в качестве серверной части был выбран PHP. Игра относится к играм тайм-менеджмента.

Была выбрана следующая схема работы:

  1. игрок выполняет действие на клиенте
  2. клиент проверяет возможность выполнения действия
  3. отправляет команду на сервер
  4. сервер проверяет возможность выполнения действия, выполняет команду, внося изменения в базу данных
  5. клиент уведомляется о том, что все в порядке или сообщает об ошибке
Все работало нормально, пока количество игроков внезапно не увеличилось.

Сначала начались тормоза на стороне php. Основная проблема этой реализации заключалась в том, что на каждое действие игрока реагирует сервер, который перед выполнением команды выполняет довольно много вычислений для расчета объектов на карте.

Эта проблема была решена путем добавления дополнительных серверов с обработчиками php. Затем мы столкнулись с производительностью MySQL. Было слишком много запросов.

Поскольку шардинг не был встроен в систему, мы выкручивались из него, как могли.

Некоторые вещи были перенесены в mongodb, а некоторые были улучшены с помощью кеша.

Кстати, mongodb оказался не таким простым хранилищем, как может показаться на первый взгляд. Учитывая, что у нас был включен шардинг и были правильные индексы, мы все равно столкнулись там с проблемами, в которых на тот момент не могли разобраться.

Время от времени в журнале просто всплывала куча запросов, которые вдруг становились скучными.

Хотя через секунду те же запросы работали нормально с тем же количеством запросов.

Но это тема для отдельного поста.

Собственно, для нового подобного проекта было решено использовать другую схему взаимодействия клиента и сервера.

Именно об этом я и хочу поговорить.

Принцип работы следующий:

  1. при запуске игра получает полное игровое состояние игрока и текущий игровой баланс
  2. игрок выполняет действия на клиенте
  3. клиент их проверяет, но не отправляет на сервер, а накапливает
  4. при достижении любого состояния клиент пакетно отправляет все эти команды на сервер
  5. сервер проверяет правильность формата команд и просто сообщает клиенту, что все в порядке
  6. в этом случае все команды сохраняются в очереди
  7. на сервере также запущен демон, который периодически извлекает пакеты команд из этой очереди и выполняет их
В результате получается схема с отложенной обработкой команд, которая имеет следующие преимущества:
  1. количество запросов к серверу значительно уменьшено
  2. уменьшено количество обращений к mysql при обработке команд, поскольку сохранение в базу данных происходит только после обработки всех команд из стека
Конечно, есть и недостатки, которые являются вполне решаемыми проблемами.

И здесь нужно смотреть требования проекта.

Наш список был следующим:

  1. получение текущего состояния игры в тот момент, когда не вся очередь для данного пользователя обработана
  2. некоторые команды требуют выполнения в реальном времени (например, покупка реальных денег)
  3. читерство игроков на клиенте (команда была выполнена на клиенте, но не выполнена на сервере)
  4. многопоточная обработка
Gearman используется в проекте в качестве сервера очередей.

В остальном всё стандартно: php+mysql+memcached. Текущая реализация включает в себя шардинг для mysql и memcached (одного memcached-сервера недостаточно).

Поговорим о том, как решаются вышеперечисленные проблемы.



На сервер было отправлено множество команд. Игрок закрыл игру и тут же запустил ее заново.

Очередь еще не обработана.

Поскольку Gearman не является базой данных и не позволяет как-то осуществлять поиск по данным, которые в ней хранятся, на PHP был написан модуль, позволяющий подключить базу данных к обработке команд и всегда знать, в каком состоянии находится обработка конкретного пользователя находится в: сколько пакетов команд в обработке и были ли ошибки при обработке.

Когда клиент запрашивает профиль игрока, у которого еще не отсортирована вся очередь, он получает в ответ сообщение с просьбой подождать и запросить профиль через 10 секунд. Это повторяется до тех пор, пока профиль не будет получен.

Согласно образцам, на выполнение всех команд игрока потребуется до 20 секунд, что вполне приемлемо.



Выполнение определенных команд в реальном времени
Наверное, это даже не проблема.

Просто реализована возможность выполнять определенные команды здесь и сейчас.

Это необходимо для того, чтобы игрок не потерял внесенные в игру деньги в случае проблем с обработкой очереди.



Обман игрока на клиенте.

Ошибки из-за различий в логике на сервере и клиенте

Ситуация: игрок обманул клиента и обманул себя на деньги.

Затем клиент разрешает ему купить здание.

Затем игрок выполняет некоторые действия со зданием.

Создается цепочка событий, которая не могла произойти, так как у игрока фактически нет денег.

Сервер получает 4 пакета команд. Второй содержит команду на покупку здания.

Начинается обработка очереди.

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

В этот момент в базу данных для этого пользователя помещается отметка времени, когда произошла ошибка.

Поскольку все пакеты команд содержат информацию о том, когда они поступили, в момент получения обработчиком 3 и 4 пакетов он их пропустит, так как время их создания меньше времени ошибки.

В этот момент клиент отправляет 5-ю партию команд, но вместо ответа, что все ок, получает запрос на перезапуск игрового состояния.

Мы знаем, когда клиент в последний раз получал состояние игры.

И если это время меньше времени последней ошибки, то сервер отказывается принимать команды на обработку.



Обработка в несколько потоков
У нас есть 3 темы.

И 3 пака команд в очереди для разных пользователей.

Процессоры просто выбирают эти пакеты и обрабатывают их одновременно.

Всё хорошо.

Ситуация усложняется, когда у нас в очереди 3 пака на одного пользователя и более 1 бесплатного обработчика.

Чтобы избежать ситуации, когда начинается параллельная обработка двух и более пакетов команд от одного игрока (их нужно обрабатывать последовательно), реализован своеобразный светофор, позволяющий сказать, что пользователь в данный момент обрабатывается.

А если это так, то второй обработчик положит данные обратно в очередь, но с более высокий приоритет .

Приоритет меняется так, что задача 2 остается впереди задачи 3. В противном случае после обработки 1 пакета процессор получит 3 пакета.

Этот светофор управляется процессорами.

В момент получения команд мы говорим, что пользователь обработан, а в конце обработки говорим, что готов.

Это, собственно, все, что я хотел вам сказать.

Не хочу углубляться в реализацию, так как ничего нетривиального там нет. В качестве фреймворка используется Yii framework. Для демона используется команда yiic, которая запускается следующим образом nohup .

/yiic que work > /dev/null & Команда следит за количеством потомков.

Запускает новые, если они по каким-то причинам выходят из строя.

Потомки регистрируют GearmanWork для анализа очереди.

Спасибо за внимание! Ставьте палец вверх и подписывайтесь :) Теги: #php #gearman #социальные игры #очередь задач #Разработка игр

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.