- PVSM.RU - https://www.pvsm.ru -
Всем привет!
Сегодня я хочу поделиться с вами решением задачи, с которой мы столкнулись при разработке социальной игры. Игровой клиент был написан на flash, а для back-end был выбран php. Игра относится к тайм-менеджмент играм.
Схема работы была выбрана следующая:
Все работало отлично, пока не происходило резкое возрастание количества игроков.
Сначала начались тормоза со стороны php. Основная проблема данной реализации заключалась в том, что на каждое действие игрока дергается сервер, который производит довольно много вычислений по обсчету объектов на карте перед выполнением команды. Эта проблема была решена путем добавления дополнительных серверов с обработчиками php.
Потом мы уперлись в производительность mysql. Было слишком много запросов. Так как шардинг не был заложен в систему, то выкручивались как могли. Что-то перенесли в mongodb, где-то улучшили работу с кэшем.
Кстати, mongodb оказался не таким простым хранилищем, как может показаться на первый взгляд. Учитывая то, что у нас было включено шардирование и стояли правильные индексы, мы все равно словили там тормоза, с которыми не смогли разобраться на тот момент. Периодически просто высыпалась пачка запросов в log, которые вдруг затупили. Хотя через секунду те же запросы работали нормально при том же количестве запросов. Но это тема отдельного поста.
Собственно для нового аналогичного проекта было принято решение использовать другую схему взаимодействия клиента и сервера.
Именно об это я и хочу рассказать.
Принцип работы следующий:
Получается схема с отложенной обработкой команд, которая имеет следующие плюсы:
Есть конечно и минусы, которые являются скорее решаемыми задачами. И тут уже нужно смотреть на требования проекта.
У нас список был следующий:
В качестве сервера очередей на проекте используется Gearman. В остальном все стандартно: php + mysql + memcached.
В текущую реализацию закладывается шардинг для mysql и memcached (одного сервера memcached бывает мало).
Давайте расскажу о том, как решаются вышеперечисленные задачи.
Так как Gearman это не бд и он не позволяет как-то искать по данным, которые хранятся внутри него, то было написан модуль на php, который позволяет подключить к обработке команд базу и всегда знать, в каком состоянии находится обработка конкретного пользователя: сколько пачек команд в обработке и, были ли ошибки в обработке.
Когда клиент запрашивает профиль игрока, для которого разобрана еще не вся очередь, он получает в ответ сообщение с предложением подождать и запросить профиль через 10 секунд. Так повторяется до тех пор, пока профиль не будет получен.
По примеркам потребуется до 20 секунд для того, чтобы выполнить все команды игрока, что допустимо.
Это скорее даже не проблема. Просто реализуется возможность определенные команды выполнять тут и сейчас. Нужно для того, чтобы игрок не потерял деньги, которые он вносит в игру в случае проблем в обработке очереди.
Ситуация: игрок подкрутил клиент и накинул себе денег. Клиент после этого позволяет ему купить здание. Затем игрок совершает какие-то действия со зданием. Создается цепочка событий, которые не могли произойти, так как денег на самом деле у игрока нет.
Сервер получает 4 пачки команд. Во второй как раз и содержится команда покупка здания.
Начинается обработка очереди. Первая пачка команд обрабатывается успешно, а во второй будет логическая ошибка, которая приводит к тому, что здание купить неудается.
В этот момент для этого пользователя в базе ставится метка времени, когда произошла ошибка. Так как во всех пачках команд содержится информация о том, когда они пришли, то в момент, когда обработчик получит 3 и 4 пачки он их пропустит, так как их время создания меньше времени ошибки.
В этот момент клиент отсылает 5 пачку команд, но вместо ответа, что все ок получает запрос на перезапуск игрового состояния. Мы знаем, когда клиент последний раз получал игровое состояние. И если это время меньше времени последней ошибки, то не сервер отказывается принимать команды на обработку.
У нас есть 3 потока. И 3 пачки команд в очереди для разных пользователей. Обработчики просто одновременно выбирают эти пачки и обрабатывают. Все отлично.
Ситуация усложняется, когда у нас в очереди 3 пачки для одного пользователя и более 1 свободного обработчика. Чтобы не возникло ситуации, в которой начинается параллельная обработка двух и более пачек команд одного игрока (их нужно обрабатывать последовательно) реализуется своеобразный светофор, который позволяет сказать, что пользователь в данный момент обрабатывается. И если это так, то второй обработчик положит данные обратно в очередь, но уже с повышенным приоритетом. Приоритет меняется для того, чтобы 2 задача осталась впереди 3. Иначе после обработки 1 пачки обработчик получит 3 пачку. Управлением этого светофора занимаются обработчики. В момент после получения команд говорим, что пользователь processed, а по окончанию обработки — ready.
Это собственно все, что хотелось рассказать. Не хочется углубляться в реализацию, так как там нет ничего нетривиального.
В качестве фреймворка используется Yii framework. Для демона используется команда для yiic, которая запускается следующим образом
nohup ./yiic que work > /dev/null &
Команда следит за количеством потомков. Запускает новых, если они по каким-то причинам падают.
Потомки регистрируют GearmanWork на разбор очереди.
Спасибо за внимание! Ставьте большие пальцы вверх и подписывайтесь :)
Автор: anonimizer_me
Источник [1]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/php-2/31958
Ссылки в тексте:
[1] Источник: http://habrahabr.ru/post/176497/
Нажмите здесь для печати.