Модернизация устаревшего программного обеспечения: программирование MUD с использованием Erlang и CloudI

Опубликовано: 2022-03-11

Что такое устаревшая модернизация?

Устаревший код повсюду. И по мере того, как скорость распространения кода продолжает экспоненциально расти, все больше и больше этого кода переводится в статус устаревшего. Во многих крупных организациях на обслуживание унаследованных систем уходит более 90% ресурсов информационных систем.

Необходимость модернизации устаревшего кода и систем для удовлетворения текущих требований к производительности и обработке широко распространена. В этом посте представлен пример использования языка программирования Erlang и основанной на Erlang сервисно-ориентированной архитектуры CloudI (SOA) для адаптации устаревшего кода — в частности, коллекции исходного кода C, созданной десятилетиями, — к 21 веку. .

Во многих крупных организациях на обслуживание унаследованных систем уходит более 90% ресурсов информационных систем.

Убийство дракона исходного кода

Много лет назад я был большим поклонником текстовых многопользовательских онлайн-игр, известных как Multi-User Dungeons (MUD). Но у них всегда были проблемы с производительностью. Я решил погрузиться в груду исходного кода C, которой уже несколько десятков лет, и посмотреть, как мы можем модернизировать этот устаревший код и довести эти ранние онлайн-игры до предела. На высоком уровне этот проект был отличным примером использования Erlang для адаптации устаревшего программного обеспечения к требованиям 21 века.

Краткое изложение:

  • Цель : взять старую видеоигру MUD с ограничением на 50 игроков и добавить в ее исходный код поддержку тысяч и тысяч одновременных подключений.
  • Проблема : Устаревший однопоточный исходный код C.
  • Решение : CloudI, сервис на основе Erlang, обеспечивающий отказоустойчивость и масштабируемость.

Модернизация устаревшего программного обеспечения: программирование MUD с использованием Erlang и CloudI

Что такое текстовый MUD?

Во всех массовых многопользовательских ролевых онлайн-играх (MMORPG), таких как World of Warcraft и EverQuest, были разработаны функции, раннее происхождение которых можно проследить до более старых текстовых многопользовательских онлайн-игр, известных как многопользовательские подземелья (MUD).

Первым MUD был Essex MUD Роя Трубшоу (или MUD1), который был первоначально разработан в 1978 году с использованием языка ассемблера MARO-10 на DEC PDP-10, но был преобразован в BCPL, предшественник языка программирования C (и работал до 1987). (Как видите, эти вещи старше большинства программистов.)

MUD постепенно набирали популярность в конце 1980-х и начале 1990-х годов благодаря различным кодовым базам MUD, написанным на C. Кодовая база DikuMUD, например, известна как корень одного из самых больших деревьев производного исходного кода MUD, по крайней мере, с 51 уникальным вариантом. на основе того же исходного кода DikuMUD. (Между прочим, за это время MUD стали также известны как «Разрушители с несколькими бакалавриатами» из-за большого количества студентов колледжей, которые не смогли бросить школу из-за своей одержимости ими.)

Проблема с устаревшими MUD

Исторический исходный код C MUD (включая DikuMUD и его варианты) изобилует проблемами производительности из-за существующих ограничений на момент его создания.

Отсутствие резьбы

В то время не было легкодоступной библиотеки потоков. Более того, многопоточность затруднила бы поддержку и модификацию исходного кода. В результате все эти MUD были однопоточными.

Каждый фрагмент кода замедляет обработку одного тика. И если какое-либо вычисление заставляет обработку занимать больше одного тика, MUD отстает, влияя на каждого подключенного игрока.

В течение одного «тика» (инкремент внутренних часов, который отслеживает ход всех игровых событий) исходный код MUD должен обрабатывать каждое игровое событие для каждого подключенного сокета. Другими словами: каждый фрагмент кода замедляет обработку одного тика. И если какое-либо вычисление заставляет обработку занимать больше одного тика, MUD отстает, влияя на каждого подключенного игрока.

При такой задержке игра сразу становится менее увлекательной. Игроки беспомощно наблюдают, как их персонажи умирают, а их собственные команды остаются необработанными.

Представляем SillyMUD

Для целей этого эксперимента по модернизации унаследованных приложений я выбрал SillyMUD, историческую производную от DikuMUD, которая повлияла на современные MMORPG и общие для них проблемы с производительностью. В 1990-х я играл в MUD, основанный на кодовой базе SillyMUD, поэтому я знал, что исходный код будет интересной и знакомой отправной точкой.

Что я унаследовал?

Исходный код SillyMUD похож на исходный код других исторических C MUD в том смысле, что он ограничен примерно 50 одновременными игроками (64, если быть точным, исходя из исходного кода).

Однако я заметил, что исходный код был изменен из соображений производительности (т. е. для увеличения ограничения одновременного проигрывателя). Конкретно:

  • В исходном коде отсутствовал поиск доменного имени по IP-адресу соединения, отсутствовавший из-за задержки, вызванной поиском доменного имени (обычно более старый MUD требует поиска доменного имени, чтобы упростить блокировку злонамеренных пользователей).
  • В исходном коде была отключена команда «donate» (немного необычно) из-за возможного создания длинных связанных списков пожертвованных предметов, которые затем требовали интенсивного обхода списков. Это, в свою очередь, снижает производительность игры для всех остальных игроков (однопоточных, помните?).

Представляем CloudI

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

CloudI обеспечивает абстракцию службы (для предоставления сервис-ориентированной архитектуры (SOA)) в Erlang, C/C++, Java, Python и Ruby, сохраняя при этом программные сбои изолированными в рамках CloudI. Отказоустойчивость обеспечивается реализацией Erlang от CloudI, основанной на отказоустойчивых функциях Erlang и реализации модели акторов. Эта отказоустойчивость является ключевой особенностью реализации CloudI Erlang, поскольку все программное обеспечение содержит ошибки.

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

Как CloudI может модернизировать устаревший текстовый MUD?

Исторический исходный код C MUD предоставляет интересную возможность для интеграции с CloudI, учитывая его проблемы с надежностью:

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

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

Какие изменения потребовались?

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

Благодаря CloudI мне удалось сохранить однопоточность исходного кода, при этом обеспечивая масштабируемость подключения к сокету.

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

Давайте рассмотрим необходимые изменения:

Консольный вывод

Буферизация вывода консоли SillyMUD (экран терминала, часто связанный с Telnet) уже была на месте, но для некоторого прямого использования файловых дескрипторов действительно требовалась буферизация (чтобы вывод консоли мог стать ответом на запрос службы CloudI).

Обработка сокетов

Обработка сокетов в исходном исходном коде основывалась на вызове функции select() для обнаружения ввода, ошибок и возможности вывода, а также для приостановки на игровой такт в 250 миллисекунд перед обработкой ожидающих игровых событий.

Интеграция CloudI SillyMUD зависит от входящих служебных запросов на ввод во время паузы с помощью функции cloudi_poll C CloudI API (на 250 миллисекунд перед обработкой тех же ожидающих игровых событий). Исходный код SillyMUD легко запускался в CloudI как служба CloudI после интеграции с API C CloudI (хотя CloudI предоставляет API как для C, так и для C++, использование C API упрощает интеграцию с исходным кодом SillyMUD на языке C).

Подписки

Интеграция CloudI подписывается на три основных шаблона имен служб для обработки событий подключения, отключения и игрового процесса. Эти шаблоны имен получены из вызова API C CloudI в исходном коде интеграции. Соответственно, либо соединения WebSocket, либо соединения Telnet имеют назначение имени службы для отправки запросов на обслуживание при установлении соединений.

Поддержка WebSocket и Telnet в CloudI обеспечивается внутренними службами CloudI ( cloudi_service_http_cowboy для поддержки WebSocket и cloudi_service_tcp для поддержки Telnet). Поскольку внутренние службы CloudI написаны на Erlang, они могут использовать исключительную масштабируемость Erlang, в то же время используя абстракцию службы CloudI, которая предоставляет функции CloudI API.

Идти вперед

Избегая обработки сокетов, меньше обработки приходится на ошибки сокетов или такие ситуации, как потеря связи (когда пользователи отключаются от сервера). Таким образом, устранение низкоуровневой обработки сокетов решило основную проблему масштабируемости.

Удаление низкоуровневой обработки сокетов решило основную проблему масштабируемости.

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

Насколько улучшился MUD?

Благодаря интеграции CloudI количество подключений увеличилось на три порядка, при этом обеспечив отказоустойчивость и повысив эффективность того же устаревшего игрового процесса.

Было три основных области улучшения:

  1. Отказоустойчивость. Благодаря модернизированной интеграции службы SillyMUD CloudI изоляция ошибок сокетов и задержки от исходного кода SillyMUD действительно обеспечивает определенную степень отказоустойчивости.
  2. Масштабируемость соединения. При использовании внутренних сервисов CloudI ограничение на одновременных пользователей SillyMUD можно легко увеличить с 64 (исторически) до 16 384 пользователей (без проблем с задержкой!) .
  3. Эффективность и производительность. Благодаря тому, что обработка соединений выполняется в CloudI, а не в однопоточном исходном коде SillyMUD, эффективность исходного кода игрового процесса SillyMUD естественным образом повышается, и он может выдерживать более высокую нагрузку.

Таким образом, простая интеграция с CloudI позволила увеличить количество подключений на три порядка, при этом обеспечив отказоустойчивость и повысив эффективность того же устаревшего игрового процесса.

Большая картина

Erlang обеспечивает 99,9999999% времени безотказной работы (менее 31,536 миллисекунд простоя в год) для производственных систем. С CloudI мы обеспечиваем такую ​​же надежность для других языков программирования и систем.

Помимо доказательства жизнеспособности этого подхода для улучшения застоявшегося исходного кода устаревшего игрового сервера (последний раз SillyMUD был изменен более 20 лет назад в 1993 году!), этот проект демонстрирует на более широком уровне, как Erlang и CloudI можно использовать для модернизации устаревших приложений и устранения ошибок. -толерантность, улучшенная производительность и высокая доступность в целом. Эти результаты обладают многообещающим потенциалом для адаптации устаревшего кода к 21 веку без необходимости капитального ремонта программного обеспечения.