- PVSM.RU - https://www.pvsm.ru -
Те, кто мог видеть мою прошлую статью [1] (а она довольно related к данной теме), знают, что вот уже больше полутора лет я разрабатываю собственную реализацию сервера Minecraft, рассчитанную, в первую очередь, на высокие нагрузки. Тем не менее, в своей работе мы используем так же и стандартный сервер (Bukkit) для нескольких мини-серверов, просто чтобы было разнообразие. И вот столкнувшись с очередной версией сервера, которая стала раз в 5 хуже предыдущих, я уже не выдержала, и решила написать эту статью.
Статья больше похожа на рассказ, чем на обучающий материал, так что вряд ли вы почерпнете из неё полезных навыков кодинга, но, надеюсь, кому-то она покажется интересной или даже полезной. Но если вы ходите увидеть кучу кода и примеров, то не открывайте статью, она не об этом. Об этом, надеюсь, будет следующая статья.
Вам не нужно знать ничего о майнкрафте и особенно о его сервере, в данной статье я хочу просто рассказать, как работает оригинальный сервер Minecraft, а так же его «обвзяка» — Bukkit, рассказать, почему такая система не работает и не должна. Я не претендую на идеальные знания о разработке серверов и не утверждаю, что мой сервер написан правильно и лучше всех. Я просто делюсь своим опытом, основанным на двух годах работы с сервером от всем известной Mojang и на полутора годах разработки своего сервера. Вся информация представленная здесь является моим личным мнением, а статья предназначена для расширения кругозора или даже обучения и может быть интересна как новичкам, так и продвинутым профессионалам.
Начнём, пожалуй, с самого чистого («ванильного») сервера Minecraft, а точнее с того, чем же он вообще занимается. Данные вещи обрабатывает сервер:
Казалось бы, все нормально и тут нет ничего преступного, все сделано довольно хорошо и добавить нечего. Проблема тут такая: всё это обрабатывается в одном основном потоке. В последних версия в Mojang почитали немного про многопоточные штуки и научились сохранять чанки на диск в отдельном потоке. Без условно это прорыв, потому что это было чертовски узкое место, давным-давно сервер сохранялся по 15 минут и на это время полностью вис, теперь такого нет. Тем не менее, проблема не решена.
Вы спросите, в чем же тут проблема? Так многие делают: основная логика приложения в одном потоке, это очень удобно программировать, не нужно заботиться о синхронизации и прочих проблемах параллельных приложений. Проблема тут в том, что если на сервере больше 40 человек, вместо стандартных 20 циклов он делает уже 15, если 70 человек, то 10, если 100 — то проседает до невероятных значений. Это при том, что у меня вообще-то мощный 6-ядерный Core i7 и 64Gb оперативной памяти! И куда мне теперь деть эти реусрсы, если из 12 потоков заняты от силы два?
Не буду пустословить, приведу пример:
На сервере 223 игрока, при этом радиус видимости выбран достаточно маленький, в памяти находится 46577 чанков, 524 «срочных» блока, 87 блоков редстоуна и поршней, 11240 Entity предметов, 4274 Entity животных, 19 тележек и лодок, 717 других Entity, игроки, которые тоже являются Entity и требуют соответствующей обработки.
Количество тайлов и обновлений света мой сервер не выводит в информации (мне это не нужно), но можете поверить, их много.
Одна лишь обработка животных ужасно тяжелый процесс — они регулярно выполняют поиск пути, поиск других Entity вокруг, у них есть AI (в последних версиях довольно продвинутый), поэтому обработать 4 тысячи животных — уже большая работа.
Обойти 3 миллиона блоков (примерно столько обрабатывается случайных блоков при таком количестве чанков) — тоже не тривиальная задача.
11 тысяч предметов надо сдвинуть, сделать некоторые другие действия, отправить обновления об их положении игрокам и прочее.
И всё это нужно успеть сделать за 50 миллисекунд, иначе все начнет тормозить, ведь скорость рассчитывается в циклах. Если сервер делает меньше циклов в секунду, чем должен, то, напрмер, мобы начинают ходить медленно и рывками. Плюс расчётов в циклах очевиден — если сервер подвиснет или произойдёт огромная сборка мусора (сервер ведь на Java), то не получится так, что тележка, едущая на полной скорости на следующем цикле превратится в быстро движущийся маленький объект, и придётся рассчитывать её движение по более сложным алгоритмам.
При этом есть ещё и Bukkit!
Bukkit — это такой «враппер» для ванильного сервера. Он добавляет API для создания плагинов, он супер-удобен для разработчиков плагинов и сделан действительно качественно. Но, грубо говоря, всё становится только хуже. Если игрок присылает пакет, что он немного сдвинулся или повернул голову… создаётся событие и посылается по всем плагинам, которые его обрабатывают. А при этом функция обработки движения и так довольно сложная. При ломании блока или установке происходит то же самое, а так же при около сотни других действий, которые создаёт игрок или сам сервер, включая махание рукой, смена состояния редстоуна, перетекание воды, спавн моба, AI, тысячи их… То есть, как бы система хорошая, но создаёт кучу дополнительных вызовов при обработке всего.
К счастью, некоторые разработчики плагинов научились вытаскивать тяжелую логику своих плагинов в отдельный поток. Яркими и хорошими примерами служат плагины OreObfuscator и Dynmap. Первый «чистит» посылаемые игроку блоки от лишних данных, чтобы игрок не мог читами смотреть сквозь стены. Он делает это в отдельном потоке, складывая пакеты в очередь и обрабатывая их отдельно от логики сервера. Второй генерирует динамическую карту для браузера, тоже очень качественно сделан. В общем, хвала им, что не нагружают основной поток ещё сильнее.
Так же есть плагин, который уменьшает количество вещей, которые обрабатывает сервер за цикл. Объединяет лежащие рядом предметы, выгружает мобов, ограничивает обработку чанков. Это очень круто, ни один сервер не обходится без этого плагина — NoLagg.
Как делать правильно (по-моему)
Мы долго мучились со всем этим, когда полтора года назад наш онлайн вырос до 100 человек, а скорость работы просела до 0.5-1 цикла в секунду. Мы пытались делать оптимизации сервера, правили код, пытались убрать как можно больше лишнего, изменили в некоторых местах работу не по циклам а по секундам (например, в печи. В Bukkit это потом тоже добавили… через несколько месяцев). В конце концов мы достигли ужасной нестабильности сервера и решили плюнуть на всё это.
Единственным вариантом, который сможет обеспечить нам комфортный онлайн такого количества игроков, какой мы хотели, был масштабируемый сервер. Не думаю, что нужно объяснять, что поток у процесса сущность не масштабируемая, может работать только на одном ядре процессора одновременно и его производительность ограничена производительностью ядра. Ядра в процессорах сейчас довольно производительные, но и работы много, да и процессоры сейчас делают многоядерными, не то время, чтобы не делать что-то многопоточным.
Разделить уже существующий сервер на несколько потоков не представляется возможным. Многопоточное программирование — штука тонкая, сложная, требующая большого знания кода, с которым работаешь, и в существующее приложение практически не встраиваемая. Код надо писать с нуля.
Так родился сервер, в основе которого было заложено как можно больше потоков: мир делится на куски по 64х64 чанка и каждый такой кусок обрабатывает чанки в одном потоке, один поток для обработки срочных блоков, один поток для редстоуна и поршней, один поток для мобов, один поток для предметов, один поток для тележек, один поток обрабатывает другие Entity и прочую информацию о мире, один поток пересчитывает свет, четыре потока по разным частям света сохраняют мир на диск, один поток рендерит карту, один поток занимается обслуживанием сервера и команд консоли, обновляет статистику. Для игроков используется система, которая позволяет обработку пакетов ставить либо на отдельный поток для каждого игрока, либо на пул потоков, либо на отдельные потоки для каждого игрока. А так же Netty (NIO) в качестве сетевого движка, в отличии от стандартного I/O.
Разработка стабильной версии такого сервера, не обладающего всем функционалом, стоило примерно 8 месяцев работы одной меня без обладания опытом. Весь код рассчитан на асинхронный доступ ко всем данным. Но оно того стоило — совсем недавно мы поставили рекорд в 559 человек, которые не просто стояли в одном месте, лагали и снимались на фрапс, а проходили очень большой ивент с редстоуном, и при этом чувствовали себя комфортно.
Мораль сей басни такова: если вы рассчитываете на то, что ваш проект будет хоть сколько-нибудь популярным и думаете, что хоть чуть-чуть теоретически возможно, что на сервере будет хоть сколько-нибудь много человек… не поскупитесь на создание масштабируемой архитектуры.
Жду ваши гнилые помидоры, предложения по улучшению этой статьи, а так же предложения по тому, что бы вы хотели увидеть в следующей статье, которая когда-нибудь будет.
Автор: Rena4ka
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/19551
Ссылки в тексте:
[1] прошлую статью: http://habrahabr.ru/post/136456/
[2] Источник: http://habrahabr.ru/post/157921/
Нажмите здесь для печати.