Наверх ▲

Антон Горохов: Здравствуйте, коллеги. Меня зовут Антон Горохов, я работаю в компании Rambler. Я занялся проектом «ТОП100» два года назад, в 2006 году. У проекта был ряд проблем, связанных с высокой нагрузкой, к тому же эти проблемы нельзя было решить быстро, не переделывая все заново.

Переделывать, конечно, не хотелось и, собственно, такой задачи не стояло. Надо было именно решить существующие проблемы с нагрузкой, как можно более аккуратно, не создавая проблем пользователям, не допуская простоев, поскольку «Рамблер TOП100» – это достаточно известный проект.

Что такое, кстати, «ТОП100»?

Напомню, если вдруг кто-то не знает или забыл. Во-первых, это каталог, где сайты разделены по тематике. Владельцы ресурсов сами добавляют туда свои сайты, получают счетчик и ставят код счетчика на свою страницу. Дальше мы считаем статистику и каждый час строим по полученной статистике рейтинги. Каждый час обновляются все тематические рейтинги и полный список ресурсов. Кроме того, мы считаем более подробную статистику для каждого ресурса. Набор статистических параметров, которые мы часто даем, на сегодняшний день довольно скромен, но, тем не менее, все это считается в реальном времени. То есть хосты, хиты и уникальные посетители за день считаются в реальном времени. Все посещенные URL за сегодня, ну и так далее.

Теперь, собственно, об устройстве «Рамблер TOП100» на лето-осень 2007 года. Значит, у нас есть сервер (он называется "counter"), который отдает счетчики и регистрирует отдачу счетчиков в log-файлах. Далее эти log-файлы поступают на сервер (мы называем его "logs"), который их пакует и делает резервные копии. И log-файлы направляются на подсчет. Есть еще сервер с названием "pages", который считает только один вид статистики - это все посещенные URL, количество хитов на каждом URL сайта. Есть еще сервер "back-end". На нем, на самом деле, находится все остальное. То есть подсчет всех уникальных посетителей на каждом ресурсе, регулярный пересчет рейтинга и веб-интерфейс, показ этого рейтинга и подобие личного кабинета пользователя. Еще у нас есть глобальная статистика, то есть статистика не по каждому ресурсу, а по всем вместе.

Какие у этой системы были проблемы два года назад?

Во-первых, на проекте имелось всего четыре сервера, причем ни один из них не был дублирован, – в общем, существовала реальная проблема с надежностью. Остро стоял и вопрос с нагрузкой – "counter" работал уже почти на пределе возможностей. Page-сообщение направлялось с обсчетом статистики и обработкой доменных URL. У сервера "back-end" еще был резерв, но тоже нужно было ожидать повышения нагрузки в связи с резервным копированием. К тому же пользователи приходили непосредственно на сервер "back-end" за статистикой и за всем остальным тоже. Если учесть еще тот факт, что "back-end" занимался практически всем, такой тугой узел получился с функциональностью. Расскажу о том, как, образно говоря, это было «развязано».

Как решались эти проблемы?

Прежде всего, надо упомянуть про мониторинг. То есть серверы уже были на мониторинге, но дополнительно было прописано еще снятие нескольких параметров, специфичных для «ТОП100». Это оказалось довольно полезно. Мониторинг позволил не только знать, что система работает, если нет оповещений о нештатной ситуации. Появилась возможность отслеживать влияние изменений.

Сначала мы работали над сервером "pages" – он уже не справлялся с обработкой. Решение мы приняли очевидное и простое: нужно этот сервер делить. Тем более, что статистики счетчиков в данном контексте независимые. То есть мы берем два-три диапазона и делим на несколько серверов.

Какая была проблема? В «ТОП100» везде используется BerkeleyDB – и для хранения статистики, и для хранения метаданных ресурсов. И как только у нас появляется больше одного сервера, надо как-то эти базы синхронизировать. Один раз в день их скопировать просто, а в течение дня есть масса задач – эта синхронизация, пользователи, регистрация новых ресурсов, изменение существующих и так далее. В общем-то, нагрузка небольшая. Можно по HTTP делать запросы. И загвоздка в том, что если какой-то из серверов "лег", это нужно как-то это отслеживать, или у пользователя будут тайм-ауты. Была реализована синхронизация по протоколу SMTP, плюс стоило учесть, что письмо может дойти на сервер, который в момент регистрации пользователя окажется выключен. Так была решена проблема с синхронизацией. Понятно, что это временный "костыль", но зато он был подставлен в нужное место и быстро.

После того, как сервер "pages" разделили, можно было и с сервером "counter" что-то делать. Было порядка двенадцати тысяч хитов в секунду в час пик, а он отвечал очень медленно. Сервер был немного "потюнен" – определенным образом настроена операционная система, параметры и так далее. Это дало выигрыш по производительности в несколько процентов, но надо было решать вопрос более кардинально. Опять же, решение мы придумали банальное – разделить нагрузку на несколько серверов. Как это сделать? Либо мы делим счетчики по диапазонам, и одни "живут" только на одном сервере "counter", а другие будут "жить" где-то на другом. Либо мы распределяем все счетчики случайным образом по нескольким серверам.

Оба варианта, в принципе, имеют право на жизнь. И если бы, например, у меня была баннерная система, я бы, наверное, выбрал первый вариант - там можно онлайн считать какую-то статистику, узнавать, сколько там было показов и так далее. Потому что синхронизировать серверы при такой нагрузке тяжеловато. В «ТОП100» нам тоже нужно показывать счетчики с цифрами. Потом мы посмотрели и выяснили, что счетчиков с цифрами у нас было порядка трех процентов, из-за слегка старомодного вида этого счетчика. Мы решили, что это не проблема. Эти три процента можно отправить на какой-то выделенный хост, где они останутся. Остальные счетчики можно распределить по серверам. Иногда мы доставляли небольшие неудобства пользователям. Пришлось разослать письма с просьбой поменять код счетчика для получения более корректных цифр.

Далее требовалось подготовить сервер "back-end", который будет делать вычисления. Нужно было его «научить» работать не с одним потоком входящих log-файлов, а с несколькими потоками. Тут вопрос «как» тоже стоял недолго. Раз уж мы претендуем на то, чтобы у нас статистика показывалась в реальном времени, то время у нас в проекте всегда должно идти вперед. То есть мы должны онлайн сливать несколько потоков по времени. В общем, это вполне реально, операция не очень дорогая.

Потом, еще интересный момент… Вот мы сейчас, допустим, ставим в два-три сервера "counter", и тут же признаем, что теряются хиты в час пик. У нас тут же возрастает нагрузка на сервер "back-end", и нам нужно к этому быть готовыми. Суть в том, что сервер "back-end" запрограммирован как демон, который может управляться командой извне. На самом деле это очень удобно. Кроме того, сервер "back-end" управлялся извне постоянно. Допустим, в ноль часов он начинал новый день. И чтобы это произошло, нужно было, грубо говоря, сидеть, ночевать с этим сервером "back-end" и ждать, когда же он досчитает сегодняшний день, чтобы не было потери статистики. Необходимо вовремя дать ему команду, чтобы он "закруглялся", когда заканчивался день. Сервер "back-end" необходимо было научить читать время из log-файла, чтобы он сам "решал", что ему сейчас нужно делать. Это оказалось удобно. Да, в течение дня мы могли допустить небольшое отставание от реального времени, т.к. за ночь могли наверстать упущенное ввиду малой нагрузки в это время суток. Это позволяло нам собрать всю статистику и иметь нормальную историю всех ресурсов. И только после того, как на сервере "back-end" были сделаны необходимые изменения, были разделены серверы "counter", что уменьшило нагрузку на них.

После этого требовалось окончательно решить вопрос с сервером "back-end". Каким образом можно было осуществить его деление на составные части? Во-первых, хотелось вывести все учетные записи, например из BerkeleyDB в MySQL. Нагрузка на MySQL была небольшой. Это не высоконагруженная часть сервиса. Был написан интерфейс для MySQL, и некоторое время базы работали параллельно. Отслеживалась их синхронность. Когда все ошибки уже были исправлены, все было переведено на MySQL.

Другая вещь, которая не позволяла серверу "back-end" делиться, – это глобальная статистика по всем ресурсам. Ее удалось экспортировать на отдельный сервер. Также на отдельный сервер был вынесен веб-интерфейс, потому что использовать сервер "back-end" для выполнения этой задачи было нерационально. Кроме того, 90 % запросов все-таки идет на главную страницу и на страницы рейтингов. А к статистике обращаются в основном владельцы ресурсов, то есть незначительная часть пользователей.

Данные уже были поделены по диапазонам так, чтобы в каждой базе было по десять тысяч счетчиков. Оставшиеся, еще не поделенные данные тоже были поделены, и после этого сервер "back-end" можно было спокойно разделять. Но нам нужно было еще и рейтинг делать эффективно. Когда все "живет" на одном сервере – проблем нет. Все ресурсы находятся в равных условиях. Как только они у нас разъехались на два-три сервера, один, допустим, "отстает", другой "догоняет", а третий "лег". Разные ситуации возможны. Поэтому нужно было как-то их синхронизировать. Опять же, была задача сохранить честность такого рейтинга. Поэтому как только оба сервера "back-end" или все серверы "back-end" досчитывают до определенной даты, они сбрасывают статистику, и мы строим рейтинг. Приходится ждать отстающих (это минус), но зато рейтинг более-менее объективный получается.

Сначала мы добавили серверы "pages" и синхронизировали базу. Потом мы добавили серверы "counter" и сняли нагрузку на отдачу счетчиков. Далее мы вынесли данные из BerkeleyDB в MySQL, настроили идентификацию и все такое, далее вынесли сервер "front-end". Он тоже дублирован на всякий случай. Потом глобальная статистика была перенесена, и потом разделили нагрузку по нескольким серверам "back-end".

Что еще я могу сказать в свою защиту?

Хочется отметить, что такое вот "деление почкованием" часто единственный способ избежать нагрузки. На самом деле это иногда не помогает, и приходится в алгоритмы тоже лезть. Деление возможно, но не до бесконечности, какие-то задачи проще решить именно через оптимизацию алгоритма. В общем, можно выделить линию на сервере.

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

А что дальше?

Про надежность я рассказал, есть механизм восстановления после сбоев. Статистики мы стали собирать чуть больше: нагрузка на серверы "counter" уменьшилась, теперь можно уделить внимание и дополнительным параметрам. Интерфейс можно улучшать в плане удобства для использования. Поскольку «Рамблер» поиском занимается, то поиск в каталоге тоже наша задача.

В общем-то, это все. Жду вопросов.

Вопросы и ответы

Вопрос из зала: Антон, насколько я вижу, нерезервированным осталось одно место – это расчет глобальной статистики. Планируется ли что-нибудь с этим делать, и если планируется, то, что и как?

Антон Горохов: Да, планируется. Я не уделил этому внимания, но туда мы поставим второй сервер. Сейчас он действительно один, и это упущение. Еще нерезервированными остались на данный момент основные серверы "back-end". Каждый из них существует в единственном экземпляре. Но на этот случай решение есть, можно в течение дня поставить в строй резервный сервер вместо выпавшего. За счет того, что мы разнесли счетчики по серверам "back-end", нагрузка на каждый из них уменьшилась, и такой сервер в течение дня довольно быстро сможет "догнать" второй и собрать то, что он там не сообщил.

Вопрос из зала: С серверами "back-end" все достаточно понятно. Именно по глобальной статистике я пока не вижу, что вы планируете делать в случае увеличения нагрузки до масштабов, когда один сервер перестанет справляться.

Антон Горохов: Да, это проблема. Пока такого решения нет. Вероятно, мы будем менять этот сервер на более мощный, обновлять его. Локальная статистика тоже считается в реальном времени. Поэтому приходится пока "жить" на одном сервере.

Вопрос из зала: И еще один вопрос. Что используется для повышения отказоустойчивости на серверах "counter"?

Антон Горохов: Там применяется симметричный закрытый интерфейс Karp.

Вопрос из зала: Получается, у вас только двенадцать серверов, которые могут "упасть", и сервис рухнет. А почему бы вам не использовать многофункциональные серверы? Скажем так, у нас есть восемь серверов, два-три выделяется на БД, остальные выполняют все функции, они будут совмещенные.

Антон Горохов: На самом деле нагрузка действительно разная. На сервере "counter" нужна только быстрая сетевая карта и процессор. Для серверов "back-end" нужен и процессор, и диски, и много памяти. Можно собрать сервер под конкретную задачу, и это будет лучше, чем если он будет универсальным. Потом, что значит "упал" «ТОП100»? Это "упали" каунтеры. Девяносто девять процентов пользователей видят, что счетчик на сайте тормозит, и понимают, что у «ТОП100» проблемы. Серверы "counter" лучше не трогать. Хочется, чтобы они занимались своим делом, они даже с MySQL не "общаются", чтобы у них не было никаких несанкционированных простоев.

Вопрос из зала: Какие протоколы взаимодействия существуют между серверами "counter" и всеми остальными частями? И каким образом происходит обновление в данный момент времени? Как я понимаю, в логах хранится вся история запросов счетчиков. Имеется ли возможность "выкатить" эти данные при "падении" сервера "counter" и потере части информации?

Антон Горохов: Взаимодействие между серверами "counter" и "back-end" протекает в виде передачи логов. Для этого была написана пара клиент-сервер. То есть на сервере "back-end" работает клиент, который запрашивает определенную порцию лога, а на сервере "counter" – сервер, который все это отдает.

Вопрос из зала: У меня был похожий вопрос. Каким образом формируется очередь между серверами "counter" и "back-end", если, например, второй из них упал.

Антон Горохов: Логи пакуются, создаются резервные копии. Т.е. теоретически мы можем восстановить утерянную информацию. Иногда нам приходиться этим заниматься. Другое дело, что мы работаем с большими объемами, поэтому предпочитаем не откладывать проблемы в дальний ящик. Если сервер "back-end" вышел из строя, то начинает функционировать другой "back-end". К этому моменту он уже содержит все необходимые копии данных. Если нет, то мы можем в течение дня поставить другой сервер "back-end", нагрузка на который будет не очень велика. С сервера "counter" он получит необходимые логи, начиная с ноля часов текущего дня, произведет подсчет и к вечеру осуществит требуемые операции. Благодаря этому, история статистики не пострадает.

Вопрос из зала: То есть один сервер "back-end" имеет дело со статистикой нескольких серверов "counter"?

Антон Горохов: Да, со статистикой всех серверов "counter".

Вопрос из зала: То есть каждый сервер "back-end" производит подсчет части данных в определенный промежуток времени с серверов "counter"?

Антон Горохов: Нет, он занимается информацией, касающейся конкретного счетчика.

Вопрос из зала: То есть если у нас информация с одного счетчика распределяется по нескольким серверам "counter", то далее она собирается в сервере "back-end"?

Антон Горохов: Совершенно верно.

Вопрос из зала: Тогда вопрос исчерпан.

Комментарии

Нет ни одного комментария

Только пользователи могут оставлять комментарии

Возможно, вам будет интересно:

Олег Илларионов

Олег Илларионов

Разработчик социальной сети ВКонтакте.

Олег Илларионов (ВКонтакте) рассказывает об изменениях принципов работы с вебом и отвечает на вопросы о самой популярной российской соцсети.

Роберт Трит (Robert Treat)

Роберт Трит (Robert Treat)

Возглавляет группу по работе с базами данных в компании OmniTI.

Роберт Трит (OMNTI) рассказывает о проблеме "раздувания" данных в Postgres.

Евгений Эльцин

Евгений Эльцин

Разработчик ПО в Google.

Евгений Эльцин (Google) рассказывает об исполнении нативного кода в браузере посредством использования Native Client.