Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Хочу все знать: просто о сложном Архитектура высоконагруженных систем на примере Facebook


Сегодня поговорим о том, как создаются технологии. Очень интересно не только то, что получается в итоге, а то, почему были приняты те или иные решения, как эти решения влияли на развитие проекта,  что делалось и почему, как развивался проект. А итогом – что имеем сейчас. Для чего это нужно? Правильно, либо понять, что тот путь был оптимальным и пойти по нему, либо сразу начать использовать уже найденные технологии, либо пойти по альтернативному пути. Мы ведь уже знаем, с какими трудностям нам придется столкнуться.

В этом отношении весьма интересны решения, которые выдерживают колоссальные нагрузки, но при этом нередко используют доступные технологии и бесплатные инструменты, к тому же они нередко предлагают свой код к ознакомлению. Встречайте, facebook. facebook использует unix-платформу, php, MySQL, кэширование, то есть все те решения, которые используются при разработке практически любого самого простого сайта. Как они со всем справляются?

Мы начинаем цикл статей, посвященный архитектуре. Забежим вперед и дадим анонс. В следующий раз мы поговорим о другом популярном решении, которое сразу создавалось как высокотехнологичное (java, oracle, resin), посмотрим, получилось ли избежать проблем?


Сегодня известны технологии построения высоконагрузочных информационных систем, называемых Storm, Akka, рекомендуются для использования распределенные базы данных и распределенные системы, параллельное программирование и многое другое интересное.  Когда и что нужно использовать? Мы начинаем интересный разговор в ходе которого будем разбираться с технологиями. Своей задачей мы видим рассказывать просто и доступно о технологиях.

Сегодня продолжим тему и поговорим о том, как cоздавался и менялся facebook. Наверное, многие из вас посмотрели замечательный фильм про создание компании facebook. Можно спорить о том, какие нужно было использовать технологии изначально, и честно или нечестно кто-то когда-то где-то поступил.  Мы поговорим об эволюции, архитектуре и технологиях.

Итак, создание facebook.

Для начала попробуем вспомнить, для чего используется  facebook (ето не использует – может представить ВКонтакте или Одноклассники). Итак, в facebook имеется личная страница пользователя, в которой имеются различные группы информации (этакие контейнер):

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

Итак, вам дали задание построить нечто похожее. Что вы сделаете? Скорее всего пойдете двумя путями: возьмете php и начнете творить, либо возьмете cms и начнете творить еще быстрее. Кстати, facebook создавался практически также. Давайте посмотрим повнимательнее.

В  далеком 2005 году facebook представлял собой достаточно простую систему:

Но основной проблемой для facebook стала необходимость пользователям в совершенно разных географических местах смотреть информацию друг друга. самое простое решение в лоб: одна база - один доступ, один сервер, но с количество изменений, количеством подключений и количеством обрабатываемых данных  обеспечить такую работу оказывается нереальным, а попробовать пользователей ограничивать доступом по территориальному принципу - нарушает принцип идеи. Наши друзья могут быть повсюду, и мы хотим быть в курсе их жизни, реагируя мгновенно на любые изменения.

Итак, что сегодня представляет собой facebook? См. цифры 2010 года

- Более 8 биллионов минут каждый день использования системы пользователями,
- Более 5 биллионов элементов контента «делится» с другими пользователями системы в неделю,
- Более 3 биллионов фотографий загружается в систему каждый месяц (Сегодня facebookодин из самых больших фото-сайтов в мире)?
- Более 80 000 приложений,
- Более 400 миллионов уникальных пользователей по всему миру.

Итак, давайте посмотрим, как было сначала и что специалисты придумали, чтобы решить все эти проблемы. И обратим внимание на годы: 2005 год - все просто, но проблемы с географией уже заявляют о себе. 2006 год - сложнее. 2006-2008 год - компания сосредоточилась на оптимизации. Для начала - оптимизация программного кода. Судя по темпам развития ей придется заниматься оптимизацией постоянно :)  2008-2010 года уже все весьма непросто, и компания продолжает заниматься оптимизацией. Что ж, они успешно справились. А мы пока посмотрим как это все было.

Что такое традиционный веб-сайт?

Связка LAMP: Linux+Apach+MySQL+PHP

Что ж, facebook использовали также такую связку. В презентации, показанной на QCon SF 2008, Aditya Agarwal рассказал об архитектуре Facebook, точнее используемый программный стек, представляя преимущества и недостатки основных компонентов: LAMP (PHP, MySQL), Memcache, Thrift, Scribe.

  • LAMP (PHP, MySQL) — стандартная связка Linux как OS, APACH, MySQL, PHP.
  • Memcached — агрессивное кэширование объектов
  • Thrift — интерфейс взаимодействия между сервисами, написанными на разных языках программирования
  • Scribe — универсальная система сбора и агрегации данных с рабочих серверов
  • ODS — инструмент для визуализации изменений любых статистических данных, имеющихся в системе — удобен для мониторинга и оповещений.

PHP

Почему? Они выбрали php и используют его. Почему?

Итак, плюсы php (помимо "так сложилось"):

- Хорошо подходит для веб-разработки,
- Легок в изучении: небольшой набор выражений и языковых конструкций,
- Легок в написании: нестрогая типизация и универсальный «массив»,
- Легок в чтении: синтаксис похож на C++ и Java,
- Быстро вносить изменения и легко отлаживать: интерпретируемый язык, для внесения изменения достаточно изменить страницу и обновить,
- Много библиотек, актуальных для веб-проектов
- Подходит для процесса разработки с короткими итерациями
- Активное сообщество разработчиков по всему миру
- Динамическая типизация, интерпретируемый язык для скриптов

 Но в ходе проекта компания столкнулась с рядом трудностей, связанных с программирование на рhp:

- слабо типизируемый язык (начиная от объявления переменной, когда в PHP не надо указывать явно тип данных у переменных. Одна и та же переменная может иметь разные типы данных в одной программе. Заканчивая необязательностью использования OOP)
- мало возможностей для анализа и сбора статистики  и оптимизации кода,
- сложно использовать для больших веб-сайтов с большим количеством страниц из-за перегрузки памяти,
- сложность совместного программирования и использования Framework (та же реализация системы контроля версий).
- линейный рост издержек при подключении файлов с исходным кодом
- высокий расход оперативной памяти и вычислительных ресурсов

Что дальше? Оптимизация (до 2008 года)

В Facebook была реализована масса доработок к PHP, в том числе улучшения в APC (ленивая загрузка, оптимизация блокировок, "подогрев" кэша) и ряд собственных расширений (клиент memcache, формат сериализации, логи, статистика, мониторинг, механизм асинхронной обработки событий).

- оптимизация программного кода,
- оптимизация загрузки данных и кэширование:
-- увеличение общей производительности путем изменения обработчика кэша на более лучший двигатель наподобие APC или Memcached. (подробнее на английском, позже вернемся к этой теме);  кэширование на стороне клиента и сервера.
-- использование паттерна Lazy Loadin (ленивая загрузка, которая поддерживает загрузку изображений по мере скроллинга страницы, в противоположность предзагрузки изображений. ) (пример)
-- оптимизация системы хранения данных в кэше.
- расширение сервисов.

MYSQL

Почему MySQL?

Исторически СУБД MySQL разрабатывалась для web-сайтов и обеспечивала быструю индексацию данных в хранилищах и оптимизацию последовательного доступа к данным (система хранения ISAM). Отличатся хорошей скоростью работы, надежностью, гибкостью. Работа с ней, как правило, не вызывает больших трудностей. Поддержка сервераMySQL автоматически включается в поставку PHP. Немаловажным фактором является ее бесплатность. MySQL распространяется на условиях общей лицензии GNU (GPL, GNU Public License).

- Быстрая и надежная база данных,
- Используется преимущественно для хранения "Ключ-значение"
- Большое количество логических узлов распределено между физическими машинами
- Балансировка нагрузке на уровне физических серверов
- Репликация для распределения операций чтения не используется
- Большинство запросов касаются самой свежей информации: оптимизация таблиц для доступа к новым данным, архивация старых записей

С какими проблемами столкнулись?

- Логическая миграция данных очень сложна
- Сложность балансировки нагрузки между серверами баз данных

Выводы:

- Создавать большое количество логических баз данных и перераспределять их между физическими узлами, балансируя таким образом нагрузку, намного удобнее
- Никаких join'ов на рабочих серверах баз данных
- Намного проще наращивать вычислительные мощности на веб-серверах, чем на серверах баз данных
- Схемы, основанные на структуре данных, делают программистов счастливыми и создают большую головную боль администраторам
- Никогда не храните не-статичные данные в централизованное базе данных
- Используйте сервисы или memcashe для глобальных запросов (пример, Какие наиболее популярные группы в моей сети контактов?

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

Основываясь на информации, что наиболее востребована самая новая информация Facebook реализовали в MySQL собственные схемы партиционирования по глобально-уникальным идентификаторам и архивирования, основанного на частоте доступа к данным. Доступ к новым данным максимально оптимизирован, а старые записи автоматически архивируются. Помимо этого используются свои библиотеки для доступа к данным на основе графа, где объекты (вершины графа) могут иметь лишь ограниченный набор типов данных (целое число, строка ограниченной длины, текст), а связи (ребра графа) автоматически реплицируются, образуя аналог распределенных внешних ключей.

Memcached

memcached — связующее программное обеспечение, реализующее сервис кэширования данных в оперативной памяти на основе парадигмы хеш-таблицы. Memcache позволило существенно ускорить решение.
 
С помощью клиентской библиотеки (для C/C++, Ruby, Perl, PHP, Python, Java, CSharp/.Net и др.) позволяет кэшировать данные в оперативной памяти множества доступных серверов. Распределение реализуется путем сегментирования данных по значению хэша ключа по аналогии с сокетами хэш-таблицы. Клиентская библиотека, используя ключ данных, вычисляет хэш и использует его для выбора соответствующего сервера. Ситуация сбоя сервера трактуется как промах кэша, что позволяет повышать отказоустойчивость комплекса за счет наращивания количества memcached серверов и возможности производить их горячую замену.
В API memcached есть только базовые функции: выбор сервера, установка и разрыв соединения, добавление, удаление, обновление и получение объекта, а также Compare-and-swap. Для каждого объекта устанавливается время жизни, от 1 секунды до бесконечности. При исчерпании памяти более старые объекты автоматически удаляются. Для PHP также есть уже готовые библиотеки PECL для работы с memcached, которые дают дополнительную функциональность.

Что поняли, что понравилось:

- Высокопроизводительная распределенная хэш-таблица
- Содержит «горячие» данные из MySQL
- Снижает нагрузку на уровень баз данных
- Основная форма кэширования
- Используется более 25TB памяти на нескольких тысячах серверов для кэша
- Среднее время отклика менее 200 микро-секунд

Как используется, какие выводы:

- Кэшируются сериализованные структуры данных PHP
- Отсутствие автоматического механизма проверки консистенции данных между memcached и MySQL — приходится делать это на уровне программного кода
- Множество multi-get запросов для получения данных на другом конце ребер графа
- Ограниченная модель данных, неэффективен для маленьких объектов.

 Улучшения, доработки, которые сделали

facebook активно занимались доработками и улучшениями,  а большая часть вошла в opensource версию memcached: порт на 64-битную архитектуру, сериализация, многопоточность, компрессия, доступ к memcache через UDP (уменьшает расход памяти благодаря отсутствию тысяч буферов TCP-соединений). В дополнение были внесены некоторые изменения в ядро Linux для оптимизации работы memcache.

- Порт на 64-битную архитектуру
- Более эффективная сериализация
- многопоточность
- Улучшенный протокол
- Компрессия
- Проксирование запросов
- Доступ к memcache через UDP:
-- снижение расхода памяти благодаря отсутствию тысяч буферов TCP соединений
-- управление ходом исполнения приложение (оптимизация multi-get )
- Статистика о работе потоков по запросу (уменьшение блокировок)
- Изменения в ядре Linux для оптимизации работы memcache:
-- распределение управления сетевыми прерывания по всем ядрам
-- оппортунистический опрос сетевых интерфейсов

После вышеперечисленных модификаций memcached способен выполнять до 250 тысяч операций в секунду, по сравнению со стандартными 30-40 тысячами.

UDP (англ. User Datagram Protocol — протокол пользовательских датаграмм) — один из ключевых элементов Transmission Control Protocol/Internet Protocol, набора сетевых протоколов для Интернета. С UDP компьютерные приложения могут посылать сообщения (в данном случае называемые датаграммами) другим хостам по IP-сети без необходимости предварительного сообщения для установки специальных каналов передачи или путей данных. UDP использует простую модель передачи, без неявных «рукопожатий» для обеспечения надёжности, упорядочивания или целостности данных. Таким образом, UDP предоставляет ненадёжный сервис, и датаграммы могут прийти не по порядку, дублироваться или вовсе исчезнуть без следа. UDP подразумевает, что проверка ошибок и исправление либо не нужны, либо должны исполняться в приложении. Чувствительные ко времени приложения часто используют UDP, так как предпочтительнее сбросить пакеты, чем ждать задержавшиеся пакеты, что может оказаться невозможным в системах реального времени. При необходимости исправления ошибок на сетевом уровне интерфейса приложение может задействовать TCP или SCTP, разработанные для этой цели. Природа UDP как протокола без сохранения состояния также полезна для серверов, отвечающих на небольшие запросы от огромного числа клиентов, например DNS и потоковые мультимедийные приложения вроде IPTV, Voice over IP, протоколы туннелирования IP и многие онлайн-игры.

Как это все работает?

 

Итак, данные при открытии страницы данные пользователя сначала ищутся в кэш, если нет - обращение в базу данных. Также ищутся данные о друзьях. Параллельно выбираются 10 последних фотографий  для всех друзей. Параллельно делается выборка наиболее последних фото. Данные возвращаются пользователю - он счастлив.

Кстати, о фотографиях. Одна из задач, с которой столкнулись в компании, заключалась в организации хранения и обработки огромного количества фотографий. Изначально фотоальбомы пользователей были организованы достаточно тривиально: при загрузке на сервер приложение создает миниатюры и сохраняет фотографии в файловой системе, а при загрузке с сервера фотографии отдаются непосредственно из файловой системы через http приложению. Этот подход обычно используют при разработке сайтов. Он позволяет быстро реализовать функциональность и проверить востребованность идеи. Идея оказалась весьма востребована. Но оказалось, что файловые системы практически непригодны для работы с большим количеством файлов. Метаданные не помещаются в оперативную память, что приводит к дополнительным обращениям к дисковой подсистеме. Ограничивающим фактором оказался ввод-вывод, а не плотность хранения или какой-то другой фактор. Для начала применили кэширование.  Наиболее часто используемые фотографии и миниатюры изображений кэшировались в памяти на оригинальных серверах для масштабируемости и производительности, а также распределялись по CDN (географически распределенной сетевой инфраструктуре) для уменьшения сетевых задержек. На первых порах это дало ожидаемый результат, но позже попробовали изображения хранить в блобах, больших бинарных файлах, предоставляя приложению информацию о том, в каком файле и с каким смещением от начала расположена каждая фотография. Такой сервис в Facebook получил название Haystack и оказался на порядок эффективнее простого изначального подхода и в три раза эффективнее оптимизированного с помощью кэш.

Философия разработки сервисов в facebook

- Создавать сервисы, когда в них реально чувствуется потребность;
- Создать общий фреймворк и инструмент для быстрой разработки сервисов (Thrift, Scribe, ODS, мониторинг сервисов);
- Использование языка, библиотеки максимально подходящей для создания именно этого сервиса.

Инновационной разработкой Facebook стал проект Thrift, механизм построения приложений с использованием нескольких языков программирования. Основная цель создания сервиса предоставить технологию прозрачного взаимодействия между разными технологиями программирования. Thrift предлагает разработчикам специальный язык описания интерфейсов, статический генератор кода, а также поддерживает множество языков, включая C++, PHP, Python, Java, Ruby, Erlang, Perl, Haskell. Возможен выбор транспорта (сокеты, файлы, буферы в памяти) и стандарта сериализации (бинарный, JSON). Поддерживаются различные типы серверов: неблокирующие, асинхронные, как однопоточные, так и многопоточные.

Альтернативными технологиями являются SOAP, CORBA, COM, Pillar, Protocol Buffers, но у всех есть свои существенные недостатки, и это вынудило Facebook разработать свою собственную: Thrift очень и очень быстрый, а также существенно снижает время разработки сетевых интерфейсов и протоколов. В Facebook технология входит в общий инструментарий, который знаком любому программисту. В частности, благодаря этому, удалось ввести четкое разделение труда: работа над высокопроизводительными серверами теперь ведется отдельно от работы над приложениями. Thrift, как и многие другие разработки Facebook, сейчас находится в открытом доступе.


- легковесный фреймоворк для разработки на разных языках программирования, разработанный с целью предоставить механизм прозрачного взаимодействия между языками программирования
- Предоставляет язык описания интерфейсов, статический генератор кода
- Поддерживаемые языки: C++, PHP, Python, Java, Ruby, Erlang, Perl, Haskell и многие другие
- Простой интерфейс для ввода-вывода (сокеты, файлы, буферы в памяти)
- Протоколы: стандарты сериализации (бинарный, JSON)
- Серверы: неблокирующие, асинхронные, как однопоточные, так и многопоточные

Scribe


- Масштабированный распределенный механизм ведения логов
- Широкая сфера применения:
-- Логи поисковых запросов
-- Публикации в новостных лентах
-- Данные по A/B тестированиям
- Более надежен, чем традиционные системы логгирования, но недостаточно надежен для транзакций баз данных
- Используется простая модель данных
- Построен на основе Thrift

Пример реализации  - новости

Как работает?

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

Выводы, которые сделали профессионалы в facebook

LAMP не совершенен, но тем не менее достаточно эффективен и пригоден для создания самых сложных систем. Конечно, PHP+MySQL+Memcache решают большинство задач, но далеко не все. Каждый крупный проект сталкивается с тем, что:

- PHP не может хранить состояния;
- PHP не самый производительный язык;
- все данные находятся удаленно.

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

В следующий раз мы поговорим о тех изменениях, которые внесли в архитектуру facebook.

Связанные статьи

Сериализация (C# и Visual Basic)
Сериализация представляет собой процесс преобразования объекта в поток байтов для хранения объекта или передачи его в память, базу данных или файл. Ее основное назначение — сохранить состояние объекта для того, чтобы иметь возможность воссоздать его при необходимости.Обратный процесс называется десериализацией.

Кэширование в PHP

Контролируемое кэширование страниц в nginx

Полезно

Программирование для мобильных устройств ― в массы, часть 1: Упрощенное введение в Android
Наверное, лучший цикл статей для знакомства с Андроидом: доступно рассказывает интересные вещи и меняет мышление в сторону программирование мобильных устройств.

Преобразование Web-приложения в мультитенантное решение SaaS
Не так страшен черт, как его малюют: как создать свое SAAS приложени

Измеряем и ускоряем сайт: инструменты на примере magento

Сервер на стероидах: FreeBSD, nginx, MySQL, PostgreSQL, PHP и многое другое

Популярно об Amazon Web Services

Об основах многопоточного программирования на языке Java

Java: Работаем с объектами


В избранное