oleg_bunin (oleg_bunin) wrote,
oleg_bunin
oleg_bunin

Categories:

Метро 2033 / Архитектура


Выполняю давно обещанное – рассказываю об январском брейн-шторме по архитектуре Метро 2033. Постараюсь сделать акцент не столько на результате, сколько на описании того, как мы думали.

Обсуждаемые под катом вопросы:
+ Какие возникают вопросы?
+ Зачем CORBA?
+ Веб-решения;
+ Узкоспециализированное хранилище vs SQL;
+ Куда коннектится клиент-флешка? Есть ли узкое место?
+ Как переносить клиента из локакции в локацию?
+ Как осуществлять коммуникацию между братиками-серверами?
+ Архитектура в общем виде;
+ Функциональная схема.

Задача – из нескольких вариантов выбрать техническую архитектуру для браузерной онлайн-игры Метро 2033. Известные на тот момент параметры игры:

  • Пространство дискретно, разбито на локации;
  • К локации привязаны инциденты (обмен, торговля, бой);
  • Инцидентов вне локации не существует;
  • Инцидент имеет конечную вместимость по персонажам (здесь многие скажут – фу, как стыдно - и будут правы – мы значительно упростили себе жизнь);
  • Количество локаций, а следовательно количество игроков в игре, потенциально не ограничено;
  • Еще одно ограничение - в локации может быть только один инцидент бой.

Эти правила дают нам возможность прогнозировать нагрузку на сервер. Итак, горизонтальную масштабируемость мы будем достигать простым маппингом массива локаций на массив серверов.

Какие возникают вопросы?

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

И вот как мы думали, отвечая на эти вопрос. Изначально решений было два. Первое – с помощью средств CORBA. Апологетом этого решения выступал Сергей Федоров - http://zmij.info/. Суть сводилась к следующему – раз уж у нас есть такое замечательное средство, как CORBA, которое решает все потенциальные проблемы оперирования распределенными объектами – давайте использовать его.

Зачем CORBA?
Легко заметить, что объект пользователь существует одновременно в нескольких экземплярах:

  • В хранилище;
  • В авторизационном сервере;
  • В инциденте;
  • В чате;
  • В локации.
  • И так далее.

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

Для подобной синхронизации была придумана следующая схема:

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

Кроме того, в тестах CORBA показывала буквально фантастические результаты. В общем все шло к ее использованию в разработке. Но я, как технический менеджер команды был против подобного решения. Почему? Факторов, которые принимает в расчет технический менеджер значительно больше, чем программист-разработчик. У него другая задача. В частности решающее влияние оказало то, что опыта использования CORBA ни у кого в команде, кроме Сергея, не было.

Из плюсов CORBA были еще продвинтые fault tolerance механизмы. Из минусов - необходимость писать конвертер внешнего xml протокола во внутренний - бинарный. В конечном счете, от CORBA отказались по той же причине, что и от своего хранилища - избыточное решение для наших задач.

Веб-решения
Альтернативный вариант технически выглядел слабее. Его апологетом выступал Павел Кудинов, pavel_kudinov, прозведший фурор на весенней РИТ-2007. Его мысль была проста – мы в вебе, давайте использовать проверенные веб-решения, проверенные временем. Если протокол передачи данных – то текстовый http, если хранилище – то SQL-база. Не будем конструировать мощный транспорт, а перестроим архитектуру так, чтобы транспорт был максимально разгружен.

Узкоспециализированное хранилище vs SQL
Да, говорил он – узкоспециализированное хранилище данных будет эффективнее универсальной базы данных, но ведь Вы его не напишите ;) У узкоспецилизированного хранилища было много плюсов. Например, мы могли схлопывать апдейты по пользователю, сохраняяя только последнюю его копию. Кроме того, структура данных в игре – объектная, маппить объектную структуру в реляционную не казалось эффективным.

Почему бы не написать маленький демон, который бы принимал запросы и осуществлял бы их сохранение?

Ответ простой – потому что это сложно. Начав писать подобную систему для Незнакомки, мы не закончили ее до сих пор. В Незнакомке все данные резервируются. Сбой, сгорание любой из машин не способно вывести проект из строя. Вопрос необходима ли такая надежность на сайте знакомств и в игре? Мой сегодняшний ответ – нет.

Система хранения в Незнакомке построена следующим образом. Приведу уже знакомую многим картинку:

В данный момент нас интересует Image Cluster. На двух машинах запущен nginx с perl’овыми модулями, которые добавляют к команде PUT еще несколько, в частности – LOCK и UNLOCK. Решив сохрнить что-либо в директорию пользователя Вы последовательно лочите все машины в кластере, затем совершаете действие – например, на все машины этого кластера закачивается файл, затем в том же порядке делается UNLOCK. Красиво? Да. Надежно? Да. Быстро? Нет.

Мы забыли об одном из правил разработки разработки, которые озвучивал на своем семинаре Александр Горный – перезаложиться, но не на бексонечность. Незнакомка перезаложена на бесконечность. Потенциально так можно хранить миллионы пользователей.

Возвращаемся к Метро. Повторять ошиби не хотелось, поэтому с полным соответствеием методам экстремального программирования (о поэтапном продвижении вперед) и с болью в душе мы согласились на реляционную базу в качестве хранилища данных.

Куда коннектится клиент-флешка? Есть ли узкое место?
Следующий вопрос, который предстояло решить – как и куда коннектится клиент-флешка? Здесь есть два принципиальных варианта – скрыть все за фронтендом или сообщить клиенту структуру серверного кластера. Считается, что первый подход более безопасный – это действительно так – вы скрываете внутреннее устройство проекта от посторонних глаз. Взамен у Вас появляется узкое место. Но, в принципе, это узкое место Вы можете распараллелить (DNS, например).

Запущенный перед тяжелым сервером nginx частично решает проблемы DDOS’а (nginx играючи держит тысячи постостоянных соединений, решает проблему медленных клиентов – в общем, читайте соответствующую презентацию и смотрите видео.

С другой стороны nginx не содержит Keep-Alive соединения с бекендом. И задосить сервер за nginx’ом все равно можно. В общем, мы решили пойти по второму пути – сообщить клиенту о структуре нашего кластера.

Как переносить клиента из локакции в локацию?
Изначально мы обдумывали два варианта взаимодействия. Тут надо вспомнить, что одна локация всегда содержит максимум один процесс боя. Поэтому основная коммуникация происходит внутри локации-демона, внутри одного процесса.

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

Переодически данные из локации сбрасываются в SQL-базу данных. Критичные данные (пользователь убит, пользователь купил патронов на 100$) сбрасываются сразу, некритичные – раз в какое-то время.

Вернемся к вопросу – как передать пользователя из локации в локацию? Вариантов несколько:

  1. Сбросить в хранилище, затем передать флешке команду переподсоединиться к новой локации. При подключении флешки-клиента новая локация-сервер обнаружит отсутствие описания объекта и считает его из хранилища.
  2. Но такой способ не разрешает ситуацию, когда флешка не подключилась к новому демону-локации. Значит надо самостоятельно подлючиться к серверу-локации и сообщить о переходе. Кто первый это сделает – флешка или братик-сервер – без разницы.

Как осуществлять коммуникацию между братиками-серверами?
То есть надо ли учитывать тот факт, что с высокой вероятностью соседняя локация обслуживается том же сервере? Может быть задействовать в этом случае shared-память, передавать через нее данные получается значительно быстрее. Минусом в данном случае является то, что у нас два протокола для решения одной и той же задачи. Второй вариант – в любом случае открыть TCP-соединение и передать информацию по бинарному или http-протоколу. Следуя заветам Ильича (pavel_kudinov) мы выбрали самый простой вариант – текстовый (XML?) протокол поверх обычного http-соединия без учета расположения локаций.

Последний вопрос – что происходит с игроками умершего сервера? Они страдают до тех пор, пока их локации не запустятся на другом физическом сервере. При этом некритичная информация будет потеряна.

Архитектура в общем виде
В общем виде результирующую архитектуру можно представить так.

Функциональная схема
Если расшифровывать схему, то нужно добавить механизмы авторизации, механизм координации сегментов, админку, логгирование.

Приятная простая схема. Надо заметить, что она получилась такой простой из-за некоторых ограничений на геймплей, которые мы ввели в самом начале. В этом и проблема ;) Сейчас эта схема уже частично неактуально именно потому, что архитекторы геймплея не смогли остаться в предложенных нами рамках.

Например, если наш мир не дискретен, не бьется на локации, а представляет собой одну большую локацию. Как вести пользователя при плавном изменении координат на большой непрерывной карте? Я просто иду влево, при этом меня плавно передают от сервера 1 к серверу 2, от сервера 2 к серверу 3. А если я не один иду? А если на границе серверов происходит стрельба? И я стреляю в человечка, которого обслуживает уже другой сервер? ;) Об этом – через пару недель.

Subscribe

  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 102 comments
Previous
← Ctrl ← Alt
Next
Ctrl → Alt →
Previous
← Ctrl ← Alt
Next
Ctrl → Alt →