LiveJournal был одним из первых сервисов, бесплатно предоставляющих всем желающим личный блог. Практически с самого начала своего существования в далеком 1999 году проект столкнулся с непрерывно растущим потоком желающих воспользоваться услугами сервиса. Как же проекту удалось справиться с предоставлением маленького кусочка интернета каждому желающему, обойдя при этом всех конкурентов?
Источники информации
Возможно Вы ожидали увидеть здесь очередной перевод статьи с английского, но тогда придется Вас разочаровать, на этот раз я решил попробовать свои силы в самостоятельном написании статьи на такую серьезную тему. Просьба особо сильно помидорами в меня не кидаться :)
Основным источником информации послужила презентация Brad Fitzpatrick в Токио.
Платформа
- Linux (Debian Sarge)
- Perl
- Apache
- MySQL 4.0/4.1 в основном с InnoDB
- Perlbal, веб-сервер и балансировщик нагрузки
- memcached для распределенного кэширования
- MogileFS, распределенная файловая система
- Gearman
- TheShwartz
- djabberd
Статистика
- на данный момент 15320315 учетных записей; (10.04.08)
- из них активно используется 551589;
- наиболее активно сервис используется в США и Российской федерации, а 2/3 пользователей - девушки и женщины;
- более 15 миллионов новых записей в блогах за месяц;
- более 50 миллионов просмотров страниц в день, при пиковой нагрузке - несколько тысяч в секунду (сильно устаревшие цифры, 2004 год);
- связь с внешним миром осуществляется через два BIG-IP (активный + в режиме ожидания) с автоматическим восстановлением работоспособности в случае сбоя в работе одного из них, защитой от DDoS, L7 набором правил, включая TCL;
- более сотни серверов, насчет конфигурации известен только тот факт, что практически на каждом сервере установлены огромные объемы оперативной памяти (более 12 GB) для эффективного кэширования.
История
- Все началось с одного обычного сервера. Он выполнял роль как веб-сервера так и базы данных. Единственный плюс такого подхода к организации работы оборудования - достаточно дешево. Само собой достаточно скоро этот сервер перестал справляться с нагрузкой.
- Следующим шагом было разнесение веб-сервера и базы данных на разные серверы, всего их получилось два. По прежнему имелось два узла, сбой в которых означал недоступность сервиса. По прежнему вычислительная мощность такой системы оставалась более чем скромной.
- Первым из тех двух серверов, как ни странно, перестал справляться с нагрузкой веб-сервер - докупили еще два. Веб-сервера три, внешний IP - один, теперь приходится как-то распределять нагрузку! А как добавить еще одну базу данных?
- Новый сервер баз данных был подключен в роли slave к исходному, данные в нем обновлялись с помощью репликации, а обрабатывал он только операции чтения, оставив все операции записи первому серверу.
- Есть предположения о том, к чему привело дальнейшее добавление новых серверов? Правильно - к полнейшему хаосу! Со временем стала возникать проблема масштабируемости баз данных. Операции чтения производились на каком-то одном сервере, но когда приходил запрос на запись данных, так или иначе данные приходилось производить обновление на каждом из slave серверов. В итоге выполнение синхронизации данных стало занимать подавляющее большинство процессорного времени slave серверов, что привело к отсутствию возможности продолжать масштабирование просто добавлением дополнительных серверов.
- Пришло время задуматься над архитектурой системы и распределением операций записи. Основной целью стало избавиться от такой серьезной избыточности данных, так как это было практически пустой тратой времени копировать одни и те же данные на десяток машин, да еще и с RAID на каждой из них.
Наиболее эффективным подходом в такой ситуации является сегментирование базы данных. Все серверы баз данных разбиваются на небольшие кластеры. Каждый пользователь системы прозрачно привязывается к определенному кластеру, таким образом когда он обновляет свой блог или какие-либо еще данные, запись ведется в рамках только небольшой группы серверов, такой же принцип справедлив и для чтения.
Применительно к LiveJournal эту схему лучше всего демонстрирует один из слайдов презентации, указанной в источниках информации:
При работе такой системы не используется auto_increment
в MySQL, а также используется составной primary key из номера пользователя и номера записи. Таким образом пространство имен объектов разбито на группы, соответствующие конкретному пользователю.
Дальнейшим развитием решения проблемы излишней избыточности данных может послужить отказ от кластеров, аналогичных по структуре исходному для хранения сегментов базы данных. Это может быть как вариант с общим на несколько серверов хранилищем данных, так и более низкоуровневая репликация данных средствами DRBD в совокупности с HeartBeat. Каждый из возможных вариантов кластеризации MySQL имеет массу положительных и отрицательных сторон, так что конкретного лидера среди них выделить достаточно сложно. Возможно именно это и подтолкнуло разработчиков построить собственное решение, комбинируя их с целью получения наилучшего эффекта.
Программное обеспечение
В ситуации, когда не удавалось найти готового программного решения для какой-то конкретной задачи, они не боялись взяться за написание его самостоятельно, это стало одним из основных компонентов успеха проекта. Существенная часть программной платформы LiveJournal написана специально для этого проекта и выпущено под свободной лицензией с открытым исходным кодом, доступным в официальном SVN репозитории.
memcached
Залогом быстрой загрузки любой страницы крупного интернет-проекта является кэширование. Но как всегда возникает вопрос: а на каком уровне обработки данных его стоит выполнять? Для динамических страниц недопустимо кэширование на уровне готовых страниц. Можно кэшировать на уровне mod_perl
, но по сути это пустая трата оперативной памяти, так как создастся отдельный кэш для каждого потока Apache, и количество промахов мимо кэша будет огромно. Кэширование запросов MySQL или HEAP таблицы также не дали бы требуемого результата ввиду чрезвычайной распределенности базы данных.
Выходом из сложившейся ситуации стало написание собственной распределенной системы кэширования объектов, получившей название memcached. Она позволяет:
- использовать для кэширования свободную оперативную память практически любого компьютера, задействованного в системе;
- кэшировать объекты практически любого языка программирования в сериализованном виде: Perl, PHP, Java, C++ и так далее;
- использовать для передачи кэшируемых данных простой протокол, не требующий избыточности данных;
- избегать даже теоретической возможности полного сбоя работы кэшируещей системы в связи с полной равнозначностью серверов;
- достигать превосходной производительности при формировании HTML-кода страниц;
- в разы снизить нагрузку на базы данных в проекте любого масштаба.
Этот продукт на практике оказался более чем эффективен, о чем свидетельствует его более чем успешное использование во многих крупнейших веб-проектах.
Perlbal
При решении вопроса, связанного с балансировкой нагрузки между веб-серверами, пришлось перепробовать далеко не один десяток готовых решений, но, к сожалению, ни один из них не смог удовлетворить все потребности проекта. Не растерявшись, разработчики написали свое решение этой задачи и назвали его Perlbal. Конкурентов у него множество, начиная от решений на уровне оборудования, например от Foundry, заканчивая proxy балансировщиками нагрузки встроенные в более популярные веб-сервера, но, тем не менее, продукт получился достаточно конкурентноспособным. Он удовлетворял всем требованиям, выдвигаемым разработчиками проекта:
- быстрый;
- небольшой размер;
- "сообразительный";
- обработка "мертвых" узлов;
- может выступать как в роли reverse proxy, так и балансировщика нагрузки;
- базовый функционал классического веб-сервера;
- реализация внутреннего перенаправления данных;
- поддержка некоторых менее существенных трюков, реализованных обычно в виде plug-in'ов.
Perlbal не так активно используется вне LiveJournal, по сравнению с memcached, но для решения конкретной задачи он подошел как нельзя лучше.
MogileFS
Идея распределенных файловых систем далеко не нова, достаточно вспомнить лишь GFS или любой ее opensource аналог. Сам факт создания такой системы был очень легок, изначальная версия была написана за одни выходные, но при доведении ее до требуемого уровня качества пришлось попотеть. Решение о ее создании было развитием идеи распределения операций записи. Общая принцип хранения файлов прост: каждый файл в ФС относится к определенному классу файлов, который определяет все правила работы с файлом, в основном механизм его реплицирования, об остальном заботится сама система.
Как и все файловые системы этого класса, MogileFS работает на уровне пользовательских приложений и использует достаточно тривиальные протокол передачи данных и общую архитектуру: клиенты, управляющие серверы, абстрактные базы данных, сервера для хранения самих данных - в этом плане ничего нового придумано не было. Доступ к файлам осуществляется с помощью HTTP-запросов PUT/GET либо через виртуальный NFS-раздел. Единственной особенностью можно назвать уклон в построение собой абстрактной прослойки между приложением и собственно кластером базы данных (в случае LiveJournal - сегмента), используемой в роли альтернативы более тривиальной master/slave схемы.
Gearman
Gearman по сути прост до безобразия, но это не мешает ему быть чрезвычайно эффективным. Возможно Вы уже догадались в чем суть этого еще одного продукта, написанного специально для LJ, если уже навели курсор на акроним в начале этого абзаца, если же нет - поясню: он управляет общей работой системы средствами клиент-серверной архитектуры и высокопроизводительного бинарного протокола. С их помощью он способен удаленно вызывать практически любые процедуры на удаленных серверах с минимальными задержками во времени. Казалось бы ничего особенного он сам по себе не делает, но на самом деле он выполняет очень важную функцию: увеличивает степень параллельности выполнения операций, необходимых для полноценного функционирования проекта. Единственное но в работе этого механизма заключается в том, что он не предоставляет никаких гарантий успешности выполнения работ.
В рамках LiveJournal Gearman применяется в основном для:
- обработка изображений средствами Image::Magick вне perl-приложений;
- создание pool'а DBI соединений (DBD::Gofer + Gearman);
- уменьшением нагрузки, создаваемой отдельными компонентами системы;
- улучшения субъективного впечатления пользователей о быстродействии сервиса, благодаря выполнению части работ параллельно в фоновом режиме;
- выполнение блокирующего ресурсы кода отдельно от обработчиков различных событий.
TheShwartz
В качестве альтернативы gearman'у для работ, для выполнения которых необходимы некоторые гарантии успешности, а также некоторая стабильность, была разработана эта библиотека. Общая схема работы осталась та же: клиент-серверная, но за стабильность приходится платить - производительность существенно ниже, возможно возникновение задержек.
Хоть эти два продукта и выполняют схожие функции, используются они обычно в совокупности друг с другом, просто-напросто обрабатывая разные типы работ.
Основными сферами применения TheShwartz в LJ являются:
- отправка электронной почты (SMTP клиент);
- LJ Notifications: каждое событие может вызывать за собой цепочку из тысяч уведомлений по электронной почте, SMS, XMPP и так далее;
- отправка RPC сообщений внешним сервисам;
- внедрение Atom потоков;
djabberd
Как всегда следуя принципу "чем проще - тем лучше", разработки LJ написали этот крошечный daemon, лежащий в основе их Jabber/LJTalk. Он способен спокойно работать с более чем 300 тысячами соединений, используя очень скромное количество оперативной памяти для поддержания каждого соединения.
Основной причиной для написания собственного Jabber-сервера, стало недостаточная расширяемость и масштабируемость существующих решений. Была необходимость в реализации многих нестандартных функций, вроде индивидуальных обработчиков пользовательских изображений и личных данных, обычно в других решениях было доступно только изменение методов аутентификации.
Подводим итоги
- Если перед Вами появилась нетривиальная задача - не бойтесь написать программное обеспечение для ее решения самостоятельно! Пускай, возможно, это потребует некторых дополнительных усилий, но масса преимуществ, связанных с полным соответствием требованиям конкретного проекта, превосходит все издержки дополнительной разработки.
- Невозможно масштабировать проект просто постоянно добавляя новые сервера, рано или поздно все же прийдется задуматься об его архитектуре;
- Распределение нагрузок и параллельное операций порой заслуживают того, чтобы разработчики обратили на них внимание;
- "Мы ненавидим изобретать колесо! Но тем не менее, если колесо не существует или оно квадратное, то мы не боимся изобретать круглое колесо." (с)