Компания
101,68
рейтинг
14 августа 2014 в 15:34

Разработка → Как мы делали XVM — популярный мод для World of Tanks. Часть вторая: развитие серверной части



Приветствуем, уважаемое хабрасообщество! Сегодня мы продолжаем начатый в первой части рассказ о создании модификации XVM (eXtended Visualization Mod) для игры World of Tanks. Во второй части вас ждет описание истории развития серверной части мода.

Древнее царство


Как я уже говорил в первой части, самый первый бэкэнд мода работал на VPS, написан был на PHP и хранил базу игроков в виде файлов в файловой системе. Чтобы избежать лимита inode-ов 64K (лимит файлов в одном каталоге обычно настраивается, но на той VPS, похоже, настройка была залочена), применялась трехуровневая структура.

Первые две буквы ника игрока становились именем первой директории от корня БД. Следующие две буквы — именем вложенной в нее директории. И уже в ней-то и лежали plain-файлы с данными игроков и именами, равными никам.

Запросы выглядели так: http://domain.com/users/<nickname>.

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

Несмотря на примитивность, у этой реализации было весомое преимущество — она работала даже на VPS и «тянула» сотню-другую запросов в секунду.

Надежда


После переезда на полноценный сервер была не менее оперативно запилена еще одна реализация, на сей раз на PHP + MySQL. Нам тогда вообще казалось, что для нашей задачи (запрос по одной записи по индексу), да еще запущенной на таком «мощном» железе (EQ4: Intel Core i7-920, 8 GB DDR3, 2x 750 GB SATA II HDD), можно использовать все антипаттерны проектирования — и оно все равно заработает. Реальность, как обычно, не подвела.

Итак, что же мы там «наколхозили»:

  1. Шаблон запроса, естественно, оставили как есть.
  2. Запрос проверялся регуляркой на похожесть на ник.
  3. Искали в БД этот ник.
  4. Если находили и он был обновлен не слишком давно, то отдавали его в ответе.
  5. Если не находили, либо запись была слишком старая, то начиналось самое интересное.

Надо было как-то получить данные игрока. На тот момент никаких публичных API не существовало, поэтому первое, что приходило в голову — парсить страницу вида worldoftanks.ru/community/accounts/27030462-Alex. Но есть одна проблема: в URL страницы, помимо ника, есть еще и ID, который мы из запроса получить не можем. Из-за этого, следующим пунктом колхоза у нас идет страница worldoftanks.ru/community/accounts, а точнее — форма поиска на этой странице. Отправляем из нашего приложения как бы AJAX–запрос, ушедший со страницы поиска игроков. В ответе получаем:

request_data: {
    echo: 0
    filtered_count: 210846
    items: [
        abbreviation: "",
        account_url: "/community/accounts/27030462-Alex/",
        battles: 3737,
        clan_url: "",
        exp: 636853,
        id: 27030462,
        name: "Alex",
        wins: 1686
    ],
    ….
}

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

К счастью, примерно тогда же появилось мобильное приложение World of Tanks Assistant, от Wargaming. Приложение вполне себе отображало статистику любого игрока. Добрые люди провели исследование и выяснили протокол обмена.

Данные грузились по адресу:
http://worldoftanks.ru/uc/accounts//api//?source_token=Intellect_Soft-WoT_Mobile-unofficial_stats
ApiVersion за время его использования нами и до появления официального API успел вырасти с 1.3 до 1.7. Каких-либо отличий для нас в них не было. Правда, проблема получения playerID прежде, чем делать запрос к API, никуда не делась.

И да, если кто еще не понял: запрос данных игрока происходил прямо во время обработки клиентского запроса.

Данный шедевр инженерной мысли стабильно работал под нагрузкой 200-300 запросов/секунду, что было лишь немногим больше, чем самый первый вариант, работавший на статических файлах. При повышении нагрузки мы упирались в CPU.

Эпоха возрождения


Поначалу моменты, когда сервер не справлялся, случались только по вечерам пятницы и субботы. Со временем эти «моменты» все увеличивались и увеличивались. Стало очевидно: что-то тут не так. Нагрузка у нас не рекордная, сервер не самый слабый. Проблема усугублялась тем, что опытных PHP-шников у нас не было. Как и не было опытных *nix специалистов.

Сели думать. Надумали следующее:

  1. Самое главное — мы занялись улучшением клиентской части, и нашли способ получать не только ники игроков, но и ID.
  2. Оптимизировать клиентские запросы, чтобы запрашивать игроков не по одному, а сразу всех из одного боя.
  3. Попробовать модную связку NodeJS + Mongo.

Был запилен первый вариант node-сервера. В нем не использовались никакие фреймворки. Только стандартные модули, типа http и mongo. Сервер представлял собой хардкорный процедурный код.

Запросы стали выглядеть так:

http://domain.com/users/<playerId1>,<playerId2>......<playerId30>


Первый же вариант показал обнадеживающие результаты — он тянул до 100-150 запросов/секунду. Новых запросов, на 30 игроков, т.е. это эквивалентно ~4000 старых запросов по одному игроку. Прирост более чем в 10 раз на смене технологии нас очень впечатлил, и, поскольку на тот момент запас прочности был очень солидным, — мы стали развивать «статистические» возможности XVM.

В очередной раз сели думать и надумали сделать что-то типа REST-сервиса со следующими методами:

  • /001
    /test — клиент при старте определяет с помощью этого метода, что сервер вообще жив. Кроме этого есть еще китайцы, которых, как известно много. Много настолько, что уже на этом этапе они создали у себя несколько серверов, используя наш код, а клиент отправлял запрос /test на каждый известный ему сервер и выбирал среди них лучший по времени отклика. Вариант /001 появился первым, так как требовал минимальной переделки клиента. Потом был оставлен для совместимости.
    Ответ:
    {
    	id: 1,
    	status: "ok"
    }

  • /:ids — основной метод для выдачи статистики в бою. :ids — это разделенный запятыми список конструкций вида playerId[=tankId], где tankId — это, как не сложно догадаться, id техники, на которой вышел в бой этот игрок. При отсутствии этого уточнения никакая статистика по технике не отдавалась — только общая.
    Вывод метода повторяет описанный ниже метод /INFO с той лишь разницей, что секция v отфильтрована по текущему танку.
  • /WN/:playerId — в клиенте не использовался, но использовался особо любопытными пользователями, так как выдавал некоторые хранящиеся в нашей базе метрики, используемые для расчета рейтингов (подробнее о различных рейтингах будет в третьей части статьи).
    Ответ:
    WN
    ID: 1015634
    Nick: iBat


    LVL: 7.4173503602768065
    EFF: 78 | 1508
    WN6: NaN |
    GWR: 56.90

    battles: 14017
    wins: 7976
    hit percent: 70
    damage: 20594320 (1469)
    frags: 18182 (1.3)
    spotted: 17163 (1.2)
    defence: 12444 (0.9)
    capture: 24638 (1.8)

    created: Wed Jan 19 2011 20:29:04 GMT+0400 (Russian Standard Time)
    updated: Wed Jul 30 2014 18:20:03 GMT+0400 (Russian Standard Time)
    cached: Thu Jul 31 2014 22:30:05 GMT+0400 (Russian Standard Time)

  • /INFO/ID/:playerId
    /INFO/:region/:playerName
    /INFO/:playerName — группа методов для “ангарной” статистики. Несколько методов были нужны из-за специфики разных песочниц в клиенте. Где-то можно было вытащить ID игрока, а где-то только имя. С переходом на Python проблема отпала — в последних версиях сервера остался только запрос по ID.
    Ответ:
    [{
    	"_id": 1015634,
    	"__v": 2,
    	"st": "ok",
    	"dt": "2014-07-31T18:30:05.761Z",
    	"ts": 1406831405761,
    	"cr": 1295454544,
    	"up": 1406730003,
    	"nm": "iBat",
    	"wgr": 8697,
    	"v": {
    		"1": {
    		"b": 20,
    		"w": 13,
    		"dmg": 13556,
    		"def": 0,
    		"frg": 35,
    		"spo": 38
    		},
    		"257": {
    		"b": 66,
    		"w": 38,
    		"dmg": 22804,
    		"def": 30,
    		"frg": 46,
    		"spo": 44
    		},
    		...
    	},
    	"rnd": {
    		"e": 1509,
    		"wn6": 1631
    	},
    	"lvl": 7.4173503602768065,
    	"b": 14017,
    	"w": 7976,
    	"spo": 17163,
    	"hip": 70,
    	"cap": 24638,
    	"dmg": 20594320,
    	"frg": 18182,
    	"def": 12444,
    	"e": 1508,
    	"wn6": 1630,
    	"wn8": 2155,
    	"cmp": {
    		"b": 50,
    		"w": 36,
    		"spo": 21,
    		"hip": 0,
    		"cap": 147,
    		"dmg": 44154,
    		"frg": 53,
    		"def": 112,
    		"e": 1393,
    		"wn6": 1394
    	}	
    }]

  • /STAT
    /STAT/queueLength
    /STAT/monthUsers — группа методов, выдававшая некоторые серверные метрики, по которым мы с помощью cacti строили графики и мониторили состояние сервера. Скриншотов cacti у нас не сохранилось, поэтому прикладываю очень похожий мониторинг, который работает в данный момент.
    Мониторинг:

Вариант разработки серверной части под такое ТЗ в процедурном стиле вызывал нехорошие мысли, поэтому решили попробовать еще одну модную штуку: express.

Надо сказать, до этого у меня не было опыта работы с каким-либо MVC-фреймворком, но, несмотря на это, разобрался очень быстро, и буквально за два вечера сделал первый работающий вариант. В общем, в адрес разработчиков express говорю самые теплые слова за возможность быстрого старта.

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

На этой волне воодушевления я решил «гулять так гулять!» и добавил в проект ODM mongoose. Код стал еще красивее, пока гонял тесты на локальной машине, буквально нарадоваться не мог, что все красиво лежит на своем месте и как все просто и логично работает.

Пришла пора деплоить эту красоту на сервер. Задеплоил. Проверил — работает! Отошел минут на 20. Прихожу, проверяю — не работает. Смотрю в консоли — процесс node запущен. Перезапускаю. Несколько секунд работает, потом — глухо.

Не буду долго тянуть: проблема была в CPU. Под обычной на тот момент вечерней нагрузкой вариант кода без mongoose шутя эту нагрузку переваривал, а вариант с mongoose (несмотря на всю свою красоту) — нет. Пришлось с грустью откатывать почти все изменения нескольких дней.

Тучи сгущаются


В конце 2012 года случился очередной всплеск активности пользователей XVM, и наш замечательный сервер перестал справляться с нагрузкой. Если честно, проблемы были и раньше, но не такие серьезные. Теперь же вечерами мод скорее не работал, чем работал. В качестве крайней меры мы даже прикрутили к серверу модуль toobusy, который позволял обрабатывать столько запросов, сколько тянуло железо, и пропускать остальное. Мониторинг показывал острую нехватку памяти для процесса mongo. Решили переезжать на другой сервер: EX-4S (i7-2600, 32 GB DDR3, 2x3 TB SATA III HDD).

Переехали. Полегчало. Правда, ненадолго.



Как только случились зимние каникулы, проблема опять возникла. Ценой всякого шаманства над кодом удалось довести скорость запросов со 150 до 180-200 в секунду, но этого все равно было мало. В какой-то из вечеров, когда все почти лежало, я ради проверки закомментировал блок кода, отвечавший за обновление “просроченных” игроков с WG API и… оно заработало. И неплохо: получили стабильные 240 с пиками до 260 запросов/сек. Через несколько дней родился код, в котором апдейтер игроков был выделен в отдельный независимый процесс, а непосредственно код, взаимодействующий с клиентами, только складывал ID игроков, требующих обновления, в отдельную коллекцию.

Этого апгрейда нам хватило где-то на месяц. И в очередной раз увидели все те же проблемы: вечерами начинались пропуски клиентских запросов. На сей раз уперлись в IOPS. Не очень долго думая, в марте 2013-го заказали для нашего сервера дополнительное оборудование: SSD-диск на 240Гб. Линейки серверов с предустановленными SSD-дисками тогда еще не было.

Помогло! Сервер стал тянуть до 380-400 запросов/сек. Примерно полгода все работало довольно ровно и не вызывало особых нареканий у пользователей. В августе 2013-го мы переехали на свежепоявившийся EX40-SSD, поскольку для наших целей он подходил лучше, а стоил дешевле, чем EX-4S с дополнительным SSD.

А в конце 2013….. как вы уже догадались, нас постигла та же самая проблема. Причем на сей раз очевидных и простых путей решения было не видно. Последние колдунства над кодом сделаны. Сервер весьма неплох. q4x2, который *nix специалист, в свою очередь, “подкрутил” сервер "по самое немогу".
Содержимое /etc/sysctl.conf для интересующихся
# Kernel sysctl configuration file for Red Hat Linux
#
# For binary values, 0 is disabled, 1 is enabled. See sysctl(8) and
# sysctl.conf(5) for more details.

# Controls IP packet forwarding
net.ipv4.ip_forward = 0

# Controls source route verification
net.ipv4.conf.default.rp_filter = 1

# Do not accept source routing
net.ipv4.conf.default.accept_source_route = 0

# Controls the System Request debugging functionality of the kernel
kernel.sysrq = 0

# Controls whether core dumps will append the PID to the core filename.
# Useful for debugging multi-threaded applications.
kernel.core_uses_pid = 1

# Controls the use of TCP syncookies
net.ipv4.tcp_syncookies = 1

# Controls the default maxmimum size of a mesage queue
kernel.msgmnb = 65536

# Controls the maximum size of a message, in bytes
kernel.msgmax = 65536

# Controls the maximum shared segment size, in bytes
kernel.shmmax = 68719476736

# Controls the maximum number of shared memory segments, in pages
kernel.shmall = 4294967296
net.core.somaxconn = 262144
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.core.netdev_max_backlog = 10000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_max_tw_buckets = 720000
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_keepalive_time = 1800
net.ipv4.tcp_keepalive_probes = 7
net.ipv4.tcp_keepalive_intvl = 30
net.core.wmem_max = 33554432
net.core.rmem_max = 33554432
net.core.rmem_default = 8388608
net.core.wmem_default = 4194394
net.ipv4.tcp_rmem = 4096 8388608 16777216
net.ipv4.tcp_wmem = 4096 4194394 16777216
net.ipv4.tcp_fin_timeout = 15
fs.epoll.max_user_watches = 1000000

fs.file-max = 5000000
net.core.netdev_max_backlog = 100000
net.core.optmem_max = 10000000
net.core.rmem_default = 10000000
net.core.rmem_max = 10000000
net.core.somaxconn = 1000000
net.core.wmem_default = 10000000
net.core.wmem_max = 10000000
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 12000
net.ipv4.tcp_max_tw_buckets = 2000000
net.ipv4.tcp_mem = 30000000 30000000 30000000
net.ipv4.tcp_rmem = 30000000 30000000 30000000
net.ipv4.tcp_wmem = 30000000 30000000 30000000
net.netfilter.nf_conntrack_max=1048576

В общем, все, что можно было сделать малой кровью, сделано. Уже начали подумывать о приобретении второго-третьего сервера и настраивать балансировку.
Но как-то вечером в разговоре с q4x2 у нас вышел спор о том, что не все платформы одинаково полезны. А если проще, то он меня убеждал, что все наши беды из-за NodeJS, и что он готов сделать свой вариант сервера на Java, который “порвет” Node на британский флаг. Я в этом сильно сомневался, но согласился поучаствовать в эксперименте, и, раз уж такое дело, решил попробовать чего-нибудь нативного.

Поскольку, по своему обыкновению, захотелось чего-то нового, то выбор пал на D и фреймворк vibed. Удивительно, но даже мне, последние года три занимавшемуся почти исключительно JS, удалось относительно быстро состряпать работающий вариант. Разрабатывал я его под Windows. А вот при разворачивании под Centos 6.5 у нас возникли очень большие сложности с удовлетворением зависимостей. Линкеру приходилось вручную прописывать очевидные вещи.

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

Пока мы все-это писали, сервер вечерами не справлялся: не хватало CPU. И мы решились на еще один переезд, на сей раз на PX90-SSD (Xeon E5-1650 v2, 64 GB ECC DDR3, 2 x 240 GB SSD).

Рассвет новой эры


Не успел Node-сервер обрадоваться двум новым ядрам, как q4x2 выкатил свой Java-сервер. Код мне напомнил самый первый вариант Node-сервера: все в одном файле в процедурном стиле. Код мы постепенно привели к божескому виду, но все равно было видно (после избавления от детских болячек), насколько этот вариант быстрее Node. В цифрах загрузки CPU — примерно в четыре раза. В часы пик, при ~500 запросах/сек основным java-процессом используется одно ядро. Для Node-версии на четырехядернике такая нагрузка была недостижимой, а на шестиядернике она была очень близка к предельной.

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

Чуть выше я упомянул про детские болячки java-кода. А пока мы их пару недель лечили, java-код работал не намного лучше старого, и было очень похоже на то, что шестиголовый сервер не потянет проксю + https.

Чтобы не ждать чуда, был немедленно приобретен еще один сервер: EX40 (i7-4770, 32 GB DDR3, 2 x 2 TB HDD) с целью быть фронтендом и тянуть https. Немцы сервер сделали, но с одной маленькой, как потом выяснилось, проблемкой: ему присвоили IP в диапазоне 5.x.x.x. Фронтенд мы на нем сделали, и все замечательно работало, но на форуме начали копиться жалобы от людей, у которых ничего не работало.

Немного исследовав этот вопрос, мы выяснили, что проблема с 5.x.x.x — известная штука, связана она с приложением под названием Hamachi. Причем новые версии Hamachi переехали на 25.x.x.x (интересно, эти ребята принципиально не хотят использовать приватные сети для своих целей?), но нам от этого было не легче. Пришлось переезжать обратно на основной сервер, благо к тому моменту он уже мог тянуть все наше хозяйство единолично, да еще с солидным запасом.

Итого на данный момент у нас весь бэкэнд: наш код + БД работает на одном сервере. Load averages редко превышают единицу. Но сейчас мы задумали еще одну реорганизацию серверного хозяйства с переносом БД на отдельный сервер. Одна из причин: у Mongo очень тяжело с холодным стартом — после перезагрузки она не тянет и половины той нагрузки, которую тянет после прогрева (из-за этого, кстати, были проблемы 3-4 августа). Перенос на отдельный сервер позволит не трогать его. Другая причина — у нас появились некоторые идеи по развитию мода, которые потребуют значительного увеличения размера БД. Текущих 2*240Гб может не хватить. В целом, можно сказать, что проблема бэкэнда на нашей стороне решена. Осталась другая.

Борьба с Wargaming API


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

Самая главная для нас проблема — лимит на число запросов с одного IP. Во времена, когда публичного API еще не было, лимит с одного IP составлял около 10 запросов/сек. Нам этого было, мягко говоря, маловато. Поэтому выкручивались как могли: у нас было несколько прокси-серверов, через которые слались запросы к API.

С появлением API стало немного легче в плане доступного лимита. Нашему приложению присвоили премиум-статус, что в теории дает нам право делать по 50 запросов/сек. А дальше начинаются НО:

  1. В старой версии (не публичный API, данные для мобильного приложения) все нужные нам данные по каждому игроку были собраны в одном методе. А с появлением публичного API данные были перераспределены по разным методам, и нам пришлось делать на каждого игрока по два запроса (/account/info/, /account/tanks/).
  2. Эти методы поддерживают запрос сразу по нескольким игрокам (до 100). То есть, в теории мы бы могли запрашивать до 5000 игроков/сек. Но в реальности запрос к /account/tanks/ на 100 игроков занимал ~45 секунд, а если их отправлять слишком часто, то они начинают массово отваливаться.
    Методом проб и ошибок был найден рецепт относительно стабильной работы: запрос на 20 игроков раз в 2 секунды. Да, это нас отбросило обратно на 10 игроков/сек. В связи с недостаточностью этой величины периоды обновления были дифференцированы для пользователей нашего мода и для всех остальных.
    Пользователей обновляем в 3-4 раза чаще. Например, сейчас пользователи попадают в очередь на обновление через 3, а остальные — через 11 суток после предыдущего обновления.
  3. Через некоторое время WG выкатил новый метод - /tanks/stats/. Метод выдавал гораздо более детальную статистику по танкам игрока по сравнению с /account/tanks/, и нам, естественно, захотелось ее получить. Но! Этот метод не поддерживает запрос по списку игроков. Только по одному. И это было еще не самое плохое: хуже, что этот метод для некоторых игроков выдавал несуществующие у него танки.
    Вот пример, отправленный в техподдержку WG, в котором видно расхождение в показаниях двух методов почти в два раза. Немного поисследовав, выяснили, что показания по реально существующим у игрока танкам — правильные. Неправильны только показания по танкам, которых нет. Для нас это означает, что теперь приходится делать по три запроса на каждый аккаунт: /account/info/, /account/tanks/, /tanks/stats/. Причем /account/tanks/ нужен только для того, чтобы получить актуальный список танков, и откинуть из результатов /tanks/stats/ лишние.
    Все это привело к тому, что сейчас скорость обновления снизилась до 8 аккаунтов/сек. Надеемся, что когда-нибудь мы сможем делать по сотне/сек, и сбудется мечта многих пользователей если не о реальном времени, то хотя бы о ежедневных обновлениях.
  4. Были и еще мелкие косячки: к примеру, после разделения немецкого танка PzIV на модификации Pz_IV_AusfD, Pz_IV_AusfH и Pz_IV_AusfA естественно изменились ID этих танков. Но еще долго API выдавал для некоторых аккаунтов старый ID=17, соответствующий неразделенному PzIV. А метод /encyclopedia/tanks/, из которого мы узнаем уровень и тип танка, этот ID уже не содержал, что иногда приводило к забавным результатам при подсчете рейтингов.

К счастью, сейчас появилась возможность отправлять багрепорты на такие чудеса. Раньше единственным ответом было: вам запрещено пользоваться чужим идентификатором приложения (?source_token=Intellect_Soft-WoT_Mobile-unofficial_stats). То есть, медленно, но верно начались изменения к лучшему. Нас стали воспринимать серьезно. Так что ждем, надеемся и верим!

На этом вторая часть повествования завершается. Впереди будет еще одна часть, которая расскажет о самом интересном - развитии клиентской части модификации.
Автор: @iBat

Комментарии (38)

  • +4
    Прекрасная история получилась, 100500 мелких проектов развиваются именно так: начинают с полного незнания того, что такое базы данных и Linux, и заканчивают вполне определенным успехом. Главное мотивация!
    • +2
      Ну нельзя сказать что мы не знаем что такое БД и Linux. :)
      Просто этот проект изначально начинался, да и продолжается до сих пор как хобби, в основном чтобы отдохнуть от правил и ограничений, которые существуют на основной работе. Так что мы тут специально позволяем себе некоторые вольности, не от незнания, а больше ради экспериментов, которые при других условиях сложно себе позволить.
  • 0
    Очень интересная история об эволюции back-end'а мода.

    А вы не пытались вести диалог с WG о том, чтоб сократить количство запросов и вернуть данные одним специальным запросом? Технически то это возможно, но вот какова политика партии?
    • +1
      Само по себе большое число запросов ни нам ни WG не мешает. Мы бы укладывались и в текущие лимиты, если бы их можно было использовать на 100%. Проблема в том, что запросы с детальной статистикой по танкам — тяжелые для серверов WG. Они уже неоднократно эти методы ускоряли (раньше было хуже). Как только скорость методов отдачи статистики станет такой, что проблема лишних запросов станет более приоритетной — будем говорить с ними и о такой оптимизации.
  • +1
    Кстати об ID пользователя, тоже в свое время заинтересовала эта проблема (году где-то в 2012). Решение нашлось, как тогда, так и сейчас работает ссылка вида worldoftanks.ru/community/accounts/named/UserName
    • 0
      Честно говоря не знал. Но парсинг HTML мне все рано не нравится.
      • 0
        тут наверно шла речь про редирект на «правильный url»
  • 0
    А еще интересно: сейчас, в бескостыльные времена public api, есть ли какие-то другие причины, помимо активации пользователем вашего мода на сайте, на то, чтобы напрягать ваш сервер обслуживать миллионы пользователей?
    Почему бы не отправлять запросы к API непосредственно с клиентского компьютера и там их обрабатывать? Будет и real-time статистика и не будет нагрузки на ваш сервер. Сервера WG возможно пострадают, но компания крупная, думаю сможет смасштабироваться.
    • 0
      К сожалению масштабы бедствия таковы, что прямые запросы от всех желающих и близко не могут быть обработаны серверами WG. И, кстати, этот вариант тоже потребует активации, только уже на сайте WG.
      • 0
        А что активировать на сайте WG? :) Вы предлагаете каждому юзеру свой токен у варгейминга получать?
        Смотрите: возьмем, скажем, API Хабрахабра и его официальное приложение для Android. Разработчик получил токен для приложения, написал его и залил в play маркет. Тысячи пользователей его скачали и пользуются, на сайте ничего не активировали, кроме подтверждения прав приложения. Также и с VK API, и с любым нормальным API.
        А проблемы нагрузки на WG, по-моему, они должны их решать, иначе зачем делать public api, которое банально не будет справляться с нагрузками? :)
        • 0
          У WG надо будет получать свой application_id, если следовать их соглашению об использовании API. Т.к. свой id светить нельзя, а для application_id=demo есть ограничения.
          Пока не обсуждали с WG такой вариант. При случае узнаем что они об этом думают.
          • 0
            Да, под токеном app_id и имел ввиду, мысли путаются уже к полвторому ночи %)
            Надеюсь, WG посмотрит как делают API большинство сервисов. В моем видении идеально оно сделано у VK.
            А вам большое спасибо за нужный и полезный мод, хоть сейчас я в WoT и не играю, но большую часть моего игрового времени я провел с XVM. И успехов вам в дальнейшей разработке! :)
    • 0
      Число попаданий в наш кэш — более 95%. Текущая архитектура WG API не потянет.
  • +3
    Классная статья, таких давно на хабре не видел, с подробностями не маркетинга а именно технических моментов. Однако мне кажется что хранение данных в монге в данном случае не оправдано, тут больше пошёл бы посгрес, и даже тот же самый мускуль с хорошо заданными индексами. По сути нужно несколько таблиц. Пользователи, танки, таблица связи пользователя с танком. В таблица с пользователями хранить пользовательскую стату, в таблице танки хранить статы танков. Мне кажется мускул тут будет шустрее монги. В танки не играю, поэтому что в статах не знаю, но в целом думаю схема будет достаточно общая. И попробуйте ещё Go Lang, по производительности он очень даже не плох, особенно если хорошо продумать его архитектуру, плюс нет таких проблем с зависимостями как у D. :)

    Еще раз большое спасибо за статью.
    • 0
      Монга как раз идеально подходит. Вернее даже задача идеально подходит для монги. Выбор NoSQL полностью оправдан. У нас тут чистейший AdHoc.
      • +1
        Вы недооцениваете возможности PostgreSQL в рамках «nosql» решений. Я бы сказал в данный момент она лучшая для этого.
        У меня на данных вида (timestamp long, id varchar(255), json blob-text, pkey вообще нет) PostgreSQL с несколько убавленой надежностью обгоняет монгу раз в 5. Если надежность не убавлять — тоже обгоняет. Если в выборке есть аггрегирование пострес справляется за секунды, а монгу мне вообще лень ждать становится. Объем данных — более 54,000,000 строк в таблице. Общий объем примерно 4 Гб, btree индекс. Машина: Core2Duo на 2.4, 6 Гб оперативной, обычный хард.
        Так что мой вам совет: бросайте монгу и переходите на Пострес.
        • 0
          Давно не следил за PostgreSQL, хотя использую ее еще с конца 90х. В частности, на POS'ах она у нас уже более 10 лет работает. Действительно, в 9.3 появилась работа с JSON, стоит посмотреть.
          • 0
            В 9.4 Json сделали бинарным, я еще на него с 9.3 не перешел, но рост в производительности обещают от 10 до 150 раз.
            Вот эти два документа должны вас окончательно убедить, я думаю:
            wiki.postgresql.org/images/b/b4/Pg-as-nosql-pgday-fosdem-2013.pdf
            www.pgcon.org/2014/schedule/attachments/318_pgcon-2014-vodka.pdf
            • 0
              PG 9.3 зарелизили только 2013-09-09, так что когда мы выбирали монгу, он еще не умел работать с JSON'ом.
              В общем, попробуем, посмотрим что получится. Спасибо за информацию.
              • 0
                Последний мой вам совет, за который меня сейчас закидают помидорами: если очень нужна скорость и есть отдельный сервер для резервного кластера, то вырубайте fsync и full_page_writes на уровне кластера и используйте unlogged таблицы. Постгрес ускоряется в 100500 раз ценой краша ВСЕХ данных в случае сбоя. Или держите два кластера: нормальный мастер для обычной работы и его полностью расторможенную копию на случай высокой нагрузки, и переключайте в случае чего. Ну и вагон памяти, настройка random_page_cost, effective_cache_size и вы забудете о mongo.
  • 0
    А клиент с патчем не может сам предоставлять необходимую информацию? Хотя такая фича не подходит — для этого необходимо чтобы все в рандоме были с патчем.
    Либо кэширование на определённое время, не думаю что статистика игрока с 10к боев поменяется, скажем, за сутки.
    Вообще при таком количестве запросов у любого API будут проблемы. Оптимизировать их можно бесконечно, всё равно будешь упираться в скорость сети.
    Логичным решением было бы либо хоститься вместе, либо разделять базу игроков, но это, думаю, вам может светить только после какой-либо коммерциализации, насчёт этого не было мыслей?
    Сам после выхода 9 версии ВОТ не играю, и хочу спросить — меня интересует — есть ли спад количества игроков и от чего он зависит? Скоро снова в школу, будет ли увеличение игроков? Ну и т.п. тенденция, плиз. Этого варгейминг никогда не скажет.
    • +1
      Когда были проблемы с обновлениями, мы думали об этом, но отказались из соображений безопасности. По определению клиент — это враг, и данные с него могут быть подделаны, что полностью скомпрометирует всю идею.
      Кэширование на клиенте не нужно — в рандоме попаданий в кэш очень мало, да и узким местом является именно WG API, а не наш сервер. Есть несколько идей как это улучшить, мы как раз обсуждаем их с WG.
      В целом, обновления статистики раз в неделю вполне достаточно, но есть психологический момент — себя, любимого, пользователи хотят обновлять чаще. Вплоть до того, что сидят часами, лишь бы увеличить рейтинг еще на единичку. Поэтому пользователей мода мы обновляем чаще.
      Спада игроков в общем-то нет, в июле немного уменьшилось, сейчас немного увеличилось, в целом особо не изменилось, количество пользователей с активированной статистикой держится в районе 1.2 млн. В статье есть скрин мониторилки, на нем в принципе все видно (только на годовом графике есть сильный спад — надо смотреть после него, а то у нас криво считалось изначально).
      • 0
        Спад на годовом связан с тем, что сперва не была включена чистка просроченных токенов. Собственно спад — это момент включения TTL индекса на коллекции токенов.
  • 0
    Скажите, а как вы отказались от Dokan? Можно подробней как обошли ограничение.
    • 0
      Впереди будет еще одна часть, которая расскажет о самом интересном — развитии клиентской части модификации.

      Наверное, скажут в третьей части :)
    • 0
      В двух словах — перешли на игровой питон, в нем нет ограничений для работы с сетью. Хотя есть и свои приколы — в частности нет поддержки SSL, и нельзя подключать бинарные либы, так что пришлось для HTTPS использовать pure-python библиотеку tlslite.
  • 0
    Нет мыслей поиграться с С/С++?
    • 0
      Мысли были, но в команде не нашлось желающих плотно этим заняться. К тому же текущий java вариант нас полностью устраивает.
  • 0
    Если вам нужны бэкендщики еще — я буду рад помочь. И да, насчет постгрес — правда попробуйте)
    • 0
      Если есть желание помочь — свяжитесь со мной в скайпе или через ПМ.
  • 0
    про финансирование написать осталось, как окупались то?
    • 0
      В первой части упоминалось: донат, ad.
  • 0
    Забавно, что начальный наколенный вариант на PHP, раздающий статику, можно было бы без проблем горизонтально отмасштабировать до 5-6к запросов в сек при том же уровне затрат.

    (И да, в том случае в VPS возникали еще и проблемы с полным количеством inode-ов в системе).
    • 0
      Ну можно было ее на уровне веб-сервера отдавать, а при cache miss уже идти до пхп
      • 0
        Так оно и раздавалось через nginx.
    • 0
      Ты просто забыл, что мы уже начали упираться в IO и стали думать насчет memcached. С монгой мы просто пошли чуть дальше. Так что про 5-6к ты немного преувеличиваешь. :)
      • 0
        Мы уперлись в inode-ы, поэтому и думали, как это обойти на том VPS
        По CPU, IO (с отключенными логами nginx, хехе) и памяти там запас был огромный + задача отлично могла бы работать на нескольких инстансах через LB.

        Про преувеличение — на прошлом месте работы прекрасно летало 3-5к запросов в минуту к динамике. :-P
        • 0
          (но это сейчас мне хорошо говорить)

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

Самое читаемое Разработка