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

Что такое твит? Это сообщение длиной до 140 символов с возможность @ссылки на пользователя и указания темы с помощью #хэш-тега. Человек публикует твиты в своем Twitter-аккаунте — их читают его подписчики (они же — follower’ы). С точки зрения архитектуры сервиса все просто и примитивно, а рабочий прототип аналогичного ресурса можно написать за час-другой, но… только если у тебя не 175 000 000 пользователей.

Twitter стартовал как небольшой побочный проект научно-исследовательской компании Odeo, но темпы его роста оказались ошеломительными. Путь от нуля до миллионов просмотров страниц занял всего несколько коротких месяцев. Ранние решения о проектировании системы неплохо справлялись с небольшими нагрузками, но они начали быстро сдавать позиции под напором огромного количества пользователей, желающих разослать всем своим друзьям весточки с ответом на простой вопрос "Чем ты занимаешься?". 140 000 000 — столько сообщений в среднем отправляют пользователи Twitter каждый день. И сервис хорошо выдерживает такую нагрузку.

Любопытная статистика

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

460 000 аккаунтов в среднем создается каждый день.

6 939 твитов составляет рекордный показатель TPS (твитов в секунду), поставленный через 4 секунды после наступления Нового года в Японии.

Обработка запросов пользователей

Активная аудитория Twitter генерирует неимоверное количество запросов через веб-страницы и программный интерфейс, который используют для своей работы все приложения-клиенты (как для десктопных, так и для мобильных ОС). Любопытно, что лишь 25% трафика приходится на веб-сайт, остальное идет через API. Это легко объяснить: только за последний год рост числа мобильных пользователей, которые активно твиттерят, составил 182%. Статистика впечатляет: 6 000 000 000 запросов к API в день, около 70 000 в секунду! Так как оба способа взаимодействия с сервисом основаны на HTTP, методы их обработки практически идентичны. Для генерации страниц используется в основном известный фреймворк Ruby on Rails, притом практически вся работа "за сценой" реализована на чистом Ruby или Scala. Многие говорят, что Ruby on Rails — далеко не самый производительный фреймворк, на что представители Twitter отвечают, что использование более быстрого решения позволило бы выиграть 10-20% в производительности, но благодаря RoR на ранних стадиях проекта был быстро реализован механизм горизонтального масштабирования. Последний позволил легко подключать новые сервера к системе без изменения кода и, как следствие, достичь роста производительности системы на несколько порядков. Сейчас проект использует более тысячи серверов, которые расположены в NTT America, однако планируется переезд в собственный датацентр. От "облаков" и виртуализации разработчики отказались с самого начала: существующие решения страдают слишком высокими задержками, особенно при доступе к дисковой подсистеме.

В роли балансировщика нагрузки используется привычный Apache httpd, но с учетом основного инструмента разработки, для обработки самих запросов необходим сервер приложений для Ruby. Для этого используется Unicorn, который имеет массу положительных сторон — например, развертывание новых версий кода без простоя, более низкое (до 30% меньше) потребление вычислительных ресурсов и оперативной памяти по сравнению с другими решениями. Связка "Apache + Unicorn" хорошо работала в начале. Но по мере развития проекта начали всплывать и серьезные недостатки решения: в подсистеме кэширования стали наблюдаться проблемы с инвалидацией (удалением устаревших данных), а ActiveRecord, автоматический генератор SQL-запросов в Ruby, как оказалось, использует не самые удачные варианты, что непременно замедляет время отклика и приводит к высоким задержкам в очереди и при репликации. Эти проблемы пришлось решать.


Архитектура средств для обработки запросов

Кэширование

При таком потоке входящих запросов очень важно всеми доступными способами снижать нагрузку на прослойку базы данных, иначе расходы на приобретение нового оборудования начнут зашкаливать. Наиболее распространенным решением в этой области является кэширование сериализованных объектов и значений, полученных ранее из базы данных или от пользователя. Они хранятся в специализированном сервисе, представляющем собой распределенную хэш-таблицу в оперативной памяти с примитивным протоколом доступа (по сути, есть два элементарных действия: "взять" и "положить"). Самым популярным решением в этой области является memcached, который заслужил доверие благодаря своей универсальности и чрезвычайно высокой производительности. Впрочем, даже тот факт, что инструмент используется практически в каждом высоконагруженном проекте, вовсе не означает, что он идеален. Вот и Twitter, используя чистый memcached, очень рано начал сталкиваться с ошибками Segmentation Fault (сбой при обращении к недоступным для программы участкам памяти). Более того, большинство стратегий кэширования основывается на длинных TTL (более минуты), а вытеснение информации делает его непригодным для хранения важных конфигурационных данных. Поэтому кэширующие сервера пришлось распределять на несколько групп для улучшения производительности и снижения риска вытеснения данных. Не обошлось без собственных доработок. В Twitter используется оптимизированная библиотека для доступа к memcached из Ruby на основе libmemcached и алгоритма хэширования FNV вместо чистого Ruby и md5.

Хранение данных

На сегодняшний день в Twitter используется множество различных систем хранения данных, у каждой из которых есть свои слабые и сильные стороны. В разных частях проекта применяется соответствующее поставленным задачам решение. Изначально для постоянного хранения твитов и других данных использовалась MySQL. Но на практике оказалось, что данные социальных сетей плохо подходят для хранения в реляционных СУБД. Этому есть немало причин: отношение "многие ко многим", сложность социального графа, необходимость обхода деревьев. Фактически использование привычных СУБД выливается в проблемы с дисковой подсистемой. Решением этих проблем стало использование FlockDB — масштабируемого хранилища для данных социального графа, построенного поверх множества серверов MySQL. Разбиение данных берет на себя сервис под названием Gizzard. Ребра графа хранятся и индексируются в обоих направлениях, помимо этого производится распределенный подсчет количества строк.

Приведу немного цифр. В базах Twitter’а содержится более 13 000 000 000 ребер графа, при этом осуществляется 20 000 операций записи и 100 000 операций чтения в секунду. Среднее время на выполнение операций в FlockDB достаточно низкое:

  • Подсчет количества строк: 1 мс
  • Временные запросы: 2 мс
  • Запись: 1 мс для журнала, 16 мс для надежной записи
  • Обход дерева: 100 граней/мс

Ранее в Twitter’е планировали для хранения всех твитов постепенно перейти на другой проект, а именно Cassandra. Решение изначально был разработано в Facebook как распределенная система хранения данных, ориентированная на работу в реальном времени. Ее основная отличительная особенность — невероятно высокая производительность на запись, но за это пришлось заплатить высокими задержками при случайном доступе к данным. Так как система децентрализована, сбои в оборудовании переносятся практически незаметно. Однако переход на Cassandra в Twitter не состоялся: стратегия по этому вопросу изменилась. Попытки использовать ее в роли основного хранилища для твитов прекратились, но она продолжает использоваться для составления аналитики в реальном времени.

Кластеры внутри Twitter

Пользователи Twitter генерируют огромное количество данных: около 15-25 Гб в минуту, то есть более 12 Тб в день. Цифра удваивается несколько раз в год. Если считать, что средняя скорость записи современного жесткого диска составляет 80 Мб в секунду, запись 12 Тб данных заняла бы почти 48 часов. На одном даже очень большом сервере данную задачу не решить. Логичным выходом стало использование кластера для хранения и анализа таких объемов данных. Подходящим решением в этой сфере оказался свободный Java-фреймворк Apache Hadoop для выполнения распределенных приложений, работающих на больших кластерах, построенных на обычном оборудовании. Hadoop прозрачно предоставляет приложениям надежность и быстродействие операций с данными. В проекте реализована вычислительная парадигма, известная как MapReduce. Согласно этой парадигме, приложение разделяется на большое количество небольших заданий, каждое из которых может быть выполнено на любом из узлов кластера. В дополнение предоставляется распределенная файловая система HDFS (Hadoop Distributed File System), использующая для хранения данных вычислительные узлы кластера, что позволяет достичь очень высокой агрегированной пропускной способности кластера. Эта система позволяет приложениям легко масштабироваться до уровня тысяч узлов и петабайт данных. Источником вдохновения для разработчиков Hadoop послужили материалы по Google File System (GFS).

Другими словами, HDFS занимается автоматической репликацией и помогает справляться со сбоями оборудования, а MapReduce позволяет обрабатывать огромные объемы данных, анализируя пары ключ-значение с помощью разработки специального кода на Java. Типичные вычислительные задачи, которые решаются с помощью Hadoop в Twitter: вычисление связей дружбы в социальном графе, подсчет статистики (количество пользователей и твитов, например подсчет количества твитов занимает 5 минут при 12 000 000 000 записей), подсчет PageRank между пользователями для вычисления репутации.

Чтобы анализировать данные с помощью MapReduce, обычно необходимо разрабатывать код на Java, что довольно трудоемко. Поэтому для упрощения обработки больших объемов данных обычно разрабатываются специализированные системы, доступные людям и без навыков программирования. Так, в Twitter используют Pig, как раз один из такого рода продуктов, предназначенный для работы с данными в Hadoop. Он представляет собой высокоуровневый язык, позволяющий трансформировать огромные наборы данных шаг за шагом. Синтаксис немного напоминает SQL, но гораздо проще, что позволяет писать в 20 раз меньше кода, чем при анализе данных с помощью обычных MapReduce-работ. Большая часть действий по анализу данных в Twitter осуществляется именно с помощью Pig. Вообще на основе Hadoop в Twitter начинают строить ряд сервисов — например, поиск людей. Для этого используется opensource распределенная система хранения данных HBase, построенная по подобию основной базы данных в Google — BigTable. По сути, она представляет собой изменяемую прослойку над HDFS, позволяющую осуществлять доступ к данным в структурированном виде. В отличие от традиционных СУБД, данные хранятся по столбцам, а не по строкам, а также для всех ячеек хранится история, то есть возможность получить данные на какой-то момент времени в прошлом, даже если они были перезаписаны.

Внутренние подпроекты Twitter

В крупных интернет-компаниях часто находятся задачи, которые не удается решить средствами готовых opensource или даже платных решений. В таких ситуациях небольшая часть команды проекта начинает разработку собственной подсистемы для решения возникшей задачи: зачастую она идет вразрез с общей платформой из-за своей специфики. С течением времени такие подпроекты становятся все более обособленными и в какой-то момент команда решает, что продукт стал достаточно зрелым, универсальным и независимым, чтобы опубликовать его как opensource. Cassandra и Scribe, например, в свое время полностью прошли этот путь в Facebook, а на сегодняшний день используются и в Twitter.

В самом Twitter ведется работа над несколькими собственными инструментами, которые, правда, еще не успели полноценно "встать на ноги" в качестве отдельных продуктов:

  • Loony — централизованная система управления оборудованием, реализованная с использованием Python, Django, MySQL и Paraminko (реализация протокола SSH на Python). Решение интегрировано с LDAP, анализирует входящую почту от дата-центра и автоматически вносит изменения в базу.
  • Murder — система развертывания кода и ПО, основанная на протоколе BitTorrent. Благодаря своей P2P-природе позволяет обновить более тысячи серверов за 30-60 секунд.
  • Kestrel — распределенная очередь, написанная на Scala и работающая по протоколу memcache. То есть, существуют две команды: "set" (поставить задачу в очередь) и "get" (взять из очереди). Из особенностей можно назвать отсутствие строгого порядка выполнения заданий и общего состояния между серверами.

Обработка статистических данных

Еще один важный тип данных — это различного рода журналы, которые необходимо не просто ввести, но еще и анализировать. Изначально для сбора логов использовали привычное для этих целей решение syslog-ng, но оно очень быстро перестало справляться с нагрузкой. Решение нашлось очень просто: программисты Facebook, столкнувшиеся с аналогичной задачей, разработали проект Scribe, который был опубликован в opensource и позже был взят на вооружение в Twitter. По сути это фреймворк для сбора и агрегации логов. Ты пишешь текст для логов и указываешь категорию записи — остальное инструмент берет на себя. Scribe работает локально и надежен даже в случае потери сетевого соединения. Каждый используемый узел знает только, на какой сервер передавать логи, что позволяет создавать масштабируемую иерархическую систему для сбора логов. Поддерживаются различные схемы для записи данных, в том числе обычные файлы и HDFS (к этой файловой системе мы вернемся ниже). Этот продукт полностью решил проблему Twitter со сбором логов, позволив логически разбить поток информации на примерно 30 категорий. В процессе использования программисты активно сотрудничали с командой Facebook, была создана и опубликована масса доработок.

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

Хороший вопрос. Рецепт от Twitter довольно прозаичен, но зато эффективен и подходит практически для любого интернет-проекта:

  • обнаружить самое слабое место в системе;
  • принять меры по его устранению;
  • перейти к следующему самому слабому месту.

На словах это может звучать довольно примитивно, но на практике нужно предпринять ряд мер, чтобы такой подход был реализуем. В первую очередь это автоматический сбор метрик (причем в агрегированном виде), построение графиков, а также сбор и анализ логов. Все данные должны появляться с минимальной задержкой, то есть как можно более близко к реальному времени. При анализе логов необходимо не просто получать информацию, а следить за динамикой показателей: стало лучше или хуже? Особенно это актуально при развертывании новых версий кода.

Золотое правило: планирование использования ресурсов намного проще, чем решение экстренных ситуаций, когда доступные ресурсы на исходе. Примерами агрегированных метрик в Twitter являются "киты" и "роботы", вернее их количество в единицу времени. Что такое "робот"? Ошибка внутри Rails (HTTP 500), непойманное исключение, проблема в коде или нулевой результат. Что такое "кит"? Это HTTP-ошибки 502 и 503, таймаут в 5 секунд (лучше кому-то показать ошибку, чем захлебнуться в запросах), убитый слишком длинный запрос к базе данных (mkill). Значительное превышение нормального количества китов или роботов в минуту является поводом для беспокойства. Механизм подсчета их количества реализован простым bash-скриптом, который просматривает агрегированные логи за последние 60 секунд, подсчитывает количество китов/роботов и рассылает уведомления, если значение оказалось выше порогового.

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

Подводим итоги

Какие рекомендации дают разработчики Twitter создателям быстро растущих стартаптов?

  1. Не бросай систему на самотек, начинай собирать метрики и их визуализировать как можно раньше.
  2. Заранее планируй рост требуемых ресурсов и свои действия в случае экстренных ситуаций.
  3. Кэшируй по максимуму все, что только возможно. Все инженерные решения не вечны, ни одно из них не идеально, но многие будут нормально работать в течение какого-то периода времени, так что заранее начинай задумываться о плане масштабирования.
  4. Не полагайся полностью на memcached и базу данных — они могут подвести в самый неподходящий момент.
  5. Все данные для запросов в реальном времени должны находиться в памяти, диски используются лишь для хранения архива.
  6. По возможности приближай вычисления к данным.

Работа Twitter далеко не всегда была гладкой. Бывали простои, сложности получения данных через API и другие проблемы. Более того, архитектура проекта сильно изменилась за последнее время. Если взять презентации разработчиков двухгодичной давности и сравнить их с положением дел сегодня, то это будут две очень разные системы. Но по-другому, вероятно, и быть не могло. Сейчас это один из наиболее стремительно растущих стартапов, на которые можно смотреть лишь с восхищением.

By Ruslan Novikov

Интернет-предприниматель. Фулстек разработчик. Маркетолог. Наставник.