Допустим, есть браузерная игра в которой можно отправить войско, чтобы оно напало на соседа. Войско придет к соседу через час, без моего участия, состоится бой, результат которого будет зависеть от конфигурации войска соседа. При этом, сосед может поменять эту конфигурацию хоть в последнюю секунду.
Т.е. я генерирую событие, которое должно быть обработано в определенное время и результат обработки будет зависеть от актуальных на момент выполнения данных.
Как лучше обрабатывать такие события на сервере?
На ум приходит очевидное — добавлять событие в очередь с параметром «время», фоновый скрипт постоянно проверяет очередь и если есть события, которым пора выполняться, обрабатывает их все по очереди.
Беспокоит меня в таком подходе то, что при большом количестве участников, событий тоже может быть много, в том числе и назначенных на одно время. Их последовательная обработка может занять время и в час Ч, результата еще не будет (т.е игра отобразит что-то там, но не результаты, которых ждут игроки).
Возможно, есть какое-то более логичное решение для отложенных событий? или не стоит переживать по поводу скорости, а делать так, чтобы обработка занимала очень мало времени?
Беспокоит меня в таком подходе то, что при большом количестве участников, событий тоже может быть много, в том числе и назначенных на одно время. Их последовательная обработка может занять время и в час Ч, результата еще не будет (т.е игра отобразит что-то там, но не результаты, которых ждут игроки).
что мешает запустить не один а несколько процессов обработки очереди.
сделай монитор очередей и если очередь имеет маленькую пропускную способность (обработка элементов очереди за ед времени) то запускай дополнительный процесс и наоборот — убиваем лишние процессы при простое
нехватку процессорного времени можно компенсировать постепенным наращиванием мощности (ставим второй/третий сервер, на втором сервере запускаем только бэдграундовские скрипты и тд...)
А если серьезно — бой должен быть моментальным? Вот прибыло войско и бац — все полегли? Сделайте просто фриз настроек обороны в момент прихода врага и введите время боя — зависящее от количества войск или стандартное, например 15 минут реального времени. Т.е. у вас будет 15 минут чтобы посчитался результат схватки. Успел посчитаться — сохраняется и все равно ждем оставшееся время. Если же когда-нибудь столкнетесь с пределом производительности — просто продлевается время боя, в случае, если еще не посчитался результат.
В данном случае должен быть моментальным. И не только потому, что у популярных игр в этом жанре так и игроки не поймут лишних задержек. Но так-же потому, что событие это не только бой, а может быть много чего еще на подобном принципе, где задержка будет сильно мешать, но на момент события нужны актуальные данные.
Теоретически, можно поставить задержку 1-2 секунды и сделать ее незаметной для пользователя. Это вариант я уже предложил выше. Но как-то это все некрасиво. А как риалтаймовые миры рассчитываются? Всякие WoW, где тысячи игроков и задержки больше пинга вообще недопустимы.
Чтобы там копаться, желательно знать где что искать и будет ли там нужное. Я не силен в чтении с++ кода и за разумное время там ничего найти не смогу. Так что это предложение в стиле поискать в гугле, для меня.
Меня интересует сам подход, принцип. и его гораздо лучше расскажет человек, который имеет опыт подобной разработки. Вот я жду, пока кто-то из таких людей зайдет в топик. Очевидные варианты я вроде уже и сам перебрал.
я конечно не буду спорить с вами, вам виднее. Но имея опыт игры в разные по жанру и качеству онлайн игры, я понимаю, что главное — грамотно подать это как фичу. Всего-то сделать красивый экран с какой-нибудь анимашкой и надпись — битва в самом разгаре, крики агонии превратились в непрерывной вой, а воины дерутся в озерах крови, пока не ясно кто одержит верх! Отправленные гонцы вернутся через 12:49 минут.
Ну в данном случае, я спрашивал совет по технической реализации. Отмазаться от пользователей в виду собственной некомпетентности я и сам понимаю что можно. Но лучше такие вещи (экраны ожидания) будут контролированными, а не из-за кривой реализации, на случай если где-то, пусть в каком-то одном, но важном месте, такое ожидание будет ну совсем не к месту.
Войско придет к соседу через час, без моего участия, состоится бой, результат которого будет зависеть от конфигурации войска соседа. При этом, сосед может поменять эту конфигурацию хоть в последнюю секунду.
кидаем событие: «войско пришло к соседу», время начало, конфигурация
разгребаем события, выбираем по времени -> время совпало:
«войско пришло к соседу»( конфигурация) -> инициация события: Бой( конфигурация 1, конфигурация 2, время )
Ну да, это я и написал. Что будет, если событий назначено 1000, пользователь сделал запрос сразу после того времени на которое назначено событие? есть Шанс что оно не будет обработано на время запроса.
в качестве очереди используй БД или такой NoSQL который позволяет выбирать по несколькои критериям (к примеру MongoDb)
выбираем самое ранне не обработанное событие из очереди и обрабатываем его (или засыпаем)
строим систему так, чтоб можно обрабатывать события параллельно несколькими скриптами
зная распределение необработанных событий во времени (в будущем) можно спланировать запуск определенного колво бэдграундных скриптов
Да я знаю как реализовать опрос очереди и как заставить работать параллельно несколько скриптов. Там, где параллельно больше, чем количество ядер (ну или 2х количество ядер), параллельность уже преимуществ не даст. Если на карте 1М игроков и 10000 событий попало на одно время, то, допустим, 20 потоков роли не сыграют, все равно 500 событий на поток это много. Ситуация с 10К событий на одно время редкая, но реальная. Должен быть какой-то иной подход, который используют в серверах реального времени.
Вопрос как-то странно задан, не понял что вам все-таки нужно… Чтобы все события гарантированно отрабатывали за заданное время? Или чтобы внутреннее состояние системы не нарушилось?
Я бы такого рода задачу решал с использованием менеджера очередей a-la RabbitMQ. Они поддерживают отложенное выполнение задач. Ну а данные о состоянии системы можно получать в момент старта задачи.
Проблема большого кол-ва юзеров решается классическим способом — увеличением кол-ва воркеров… Получать задачи от RabbitMQ могут несколько серверов-воркеров, так что параллелить можно в очень широких пределах.
Для вашего примера: записываете в очередь отложенное событие «напади на соседа». В час Ч эта задача запускается на каком-то из воркеров, берет из БД текущую конфигурацию войск противника и считает результат боя. После обсчета сохраняет в БД или информирует юзера или запускает другую задачу в очередь…
только RabbitMQ — это брокер очередей
а в данном случае необходимо выбрать из очереди событие на конкретной момент времени,
что-то типа очереди с приоритетами
такой возможности в кролике нет В час Ч эта задача запускается на каком-то из воркеров,
как определить этот час Ч?
брать инфу из одной очереди и пихать в другую (на обнове БД) с приоритетами — это бред!!!
Вопрос задан тем, кто имеет опыт разработки серверов онлайн-игр. О том, какие подходы к данной задаче использовали и на каком остановились. К сожалению, мне пока советую либо костыли (нарисуй анимашку и пусть себе тупит), либо то, о чем я не спрашивал (как реализовать очередь итд. я и сам знаю, ну или спрошу отдельно. Я спрашивал о том, какие есть решения кроме очереди или о том, как так обрабатывать очередь, чтобы получать результат незамедлительно), либо вообще бред (типа, купи еще железа). Видимо, разработчики онлайн-игр в данных раздел не заглядывают(
Я работаю в конторе, которая делает а основном онлайн-игры. Но я над смежной задачей работаю, но использую наработки из движка игры. Знаю, что игроделы у нас используют Celery + RabbitMQ. Celery умеет делать отложенные задачи. Если надо — могу поподробнее расспросить в понедельник…
Сергей: а вы можете подсказать, как сделать чтобы внутреннее состояние системы не нарушилось? У меня похожая задача. Мне нужно записать данные в бд и через определенное время выполнить с ними некие действия. Как можно гарантировать, что при записи данных в бд создастся и сообщение в rabbitmq? Чтобы избежать ситуации, когда данные в бд записались, а сообщение в rabbitmq не создалось. То есть мне нужна атомарность, либо всё (запись данных и сообщение в rabbitmq) либо ничего. Это можно как-то сделать? В качестве бд используется mysql.
2.1) Если время X не наступило, а процессор простаивает, рассчитываем исход битвы, но сохраняем результат во временную таблицу.
2.2) Если пользователь изменил условия, уничтожаем сохраненный результат во временной таблице.
Наступил час X
3.1) Если время X настало и во временной таблице есть рассчитанные данные, то копируем их в основное состояние игры.
3.2) Если время X настало и временная таблица пуста, то рассчитываем исход битвы.
Если процессор обрабатывал задание «на будущее», то он должен его бросить и начать обрабатывать критичное. А если он занимается обработкой критичного задания, то ничего не поделаешь, будет лаг.
Моё предложение как раз и заключается в том, что бы обработать все данные заранее. А если условия будут изменяться, то заново перерасчитывать с даты изменения.
Задание поступило — ушло в базу. Вне зависимости от авторизации игрока, персонажи выполняют задачу. Ходы можно инициализировать и просчитывать по крону.
Вряд-ли можно говорить о каком-то существенном увеличении потребления ресурсов в этом случае, в сравнении с той ресурсоемкостью, которая в любом случае будет присутствовать при любых других решениях.