Архитектура Facebook
На сегодняшний день Facebook является пожалуй самым обсуждаемым интернет-проектом во всем мире. Не смотря на довольно низкий уровень проникновения Facebook в России, темпы захвата аудитории этим проектом мягко говоря поражают. Как же им удается управляться с таким огромным социальным графом и удовлетворять потребности в общении невероятно большого количества людей по всему миру?
Платформа
Linux — операционная система
PHP с HipHop — код на PHP компилируется в C++
memcached — агрессивное кэширование объектов
MySQL — используется как хранилище пар ключ-значение, никаких join'ов
Thrift — интерфейс взаимодействия между сервисами, написанными на разных языках программирования
Scribe — универсальная система сбора и агрегации данных с рабочих серверов
Статистика
Более 500 миллионов активных пользователей (месячная аудитория)
Более миллиарда социальных связей
Более 200 миллиардов просмотров страниц в месяц
Более 4 триллионов действий попадает в новостные ленты каждый день
Более 150 миллионов обращений к кэшу в секунду; 2 триллиона объектов в кэше
Более 8 миллиардов минут провели пользователи на Facebook'е ежедневно
Более 3 миллиардов фотографий загружается каждый месяц, до 1.2 миллиона фотографий в секунду
20 миллиардов фотографий в 4 разрешениях = 80 миллиардов фотографий, их бы хватило чтобы покрыть поверхность земли в 10 слоев; это больше, чем на всех других фото-ресурсах в месте взятых
О более чем 5 миллиардах единиц контента рассказывается друзьям еженедельно
Более миллиарда сообщений в чате каждый день
Более ста миллионов поисковых запросов в день
Более 250 приложений и 80 тысяч сторонних ресурсов на платформе Facebook Connect
Более 400 тысяч разработчиков сторонних приложений
Менее 500 разработчиков и системных администраторов в штате
Более миллиона активных пользователей на одного инженера
Десятки тысяч серверов, десятки гигабит трафика
Архитектура
Общие принципы
Балансировщик нагрузки выбирает веб-сервер для обработки запроса
PHP-код в веб-сервере подготавливает HTML, пользуясь данными из различных источников:
MySQL
memcached
Специализированные сервисы
Если взглянуть с другой стороны, то получим трехуровневую архитектуру:
Вер-приложение
Распределенный индекс
Постоянное хранилище
Использование открытых технологий там, где это возможно
Поиск возможностей оптимизации используемых продуктов
Философия Unix:
Старайтесь делать каждый компонент системы простым и производительным
Комбинируйте компоненты для решения задач
Концентрируйте внимание на хорошо обозначенных точках взаимодействия
Все усилия направлены на масштабируемость
Попытки минимизации количества точек отказа
Простота, простота, простота!
PHP
Почему PHP?
Во многом «так исторически сложилось»
Хорошо подходит для веб-разработки
Легок в изучении: небольшой набор выражений и языковых конструкций
Легок в написании: нестрогая типизация и универсальный «массив»
Легок в чтении: синтаксис похож на C++ и Java
Прост в дебаггинге: нет необходимости в перекомпиляции
Большой ассортимент библиотек, актуальных для веб-проектов
Подходит для процесса разработки с короткими итерациями
Активное сообщество разработчиков по всему миру
Динамическая типизация, интерпретируемый язык для скриптов
Как оказалось на самом деле?
Высокий расход оперативной памяти и вычислительных ресурсов
Сложно работать, когда объем исходного кода очень велик: слабая типизация и ограниченные возможности для статичного анализа и оптимизации кода
Не особо оптимизирован для использования в крупных проектах
Линейный рост издержек при подключении файлов с исходным кодом
Механизм разработки расширений не очень удобен
Доработки:
Оптимизация байт-кода
Улучшения в APC (ленивая загрузка, оптимизация блокировок, «подогрев» кэша)
Свои расширения (клиент memcache, формат сериализации, логи, статистика, мониторинг, механизм асинхронной обработки событий)
HipHop — трансформатор исходных кодов:
Разработчики пишут на PHP, который конвертируется в оптимизированный C++
Статический анализ, определение типов данных, генерация кода, и.т.д.
Облегчает разработку расширений
Существенно сокращает расходы оперативной памяти и вычислительных ресурсов
У команды из трех программистов ушло полтора года на разработку, переписаны большая часть интерпретатора и многие расширения языка
Опубликован под opensource лицензией в начале года, нет необходимости проходить этот же путь с нуля
MySQL
Как используется MySQL?
Используется как хранилище пар ключ-значение
Большое количество логических узлов распределено между физическими машинами
Балансировка нагрузке на уровне физических серверов
Репликация для распределения операций чтения не используется
Большинство запросов касаются самой свежей информации: оптимизация таблиц для доступа к новым данным, архивация старых записей
В целом быстро и надежно
Как оказалось на самом деле?
Логическая миграция данных очень сложна
Создавать большое количество логических баз данных и перераспределять их между физическими узлами, балансируя таким образом нагрузку, намного удобнее
Никаких join'ов на рабочих серверах баз данных
Намного проще наращивать вычислительные мощности на веб-серверах, чем на серверах баз данных
Схемы, основанные на структуре данных, делают программистов счастливыми и создают большую головную боль администраторам
Никогда не храните не-статичные данные в централизованное базе данных
Доработки:
Практически никаких модификаций исходного кода MySQL
Своя схема партиционирования с глобально-уникальными идентификаторами
Своя схема архивирования, основанная на частоте доступа к данным относительно каждого пользователя
Расширенный движок запросов для репликации между датацентрами и поддержания консистенции кеша
Библиотеки для доступа к данным на основе графа:
Объекты (вершины графа) с ограниченными типами данных (целое число, строка ограниченно длины, текст)
Реплицированные связи (ребра графа)
Аналоги распределенных внешних ключей (foreign keys)
Большинство данных распределено случайно
Memcache
Как используется memcached?
Высокопроизводительная распределенная хэш-таблица
Содержит «горячие» данные из MySQL
Снижает нагрузку на уровень баз данных
Основная форма кэширования
Используется более 25TB памяти на нескольких тысячах серверов
Среднее время отклика менее 250 микро-секунд
Кэшируются сериализованные структуры данных PHP
Отсутствие автоматического механизма проверки консистенции данных между memcached и MySQL — приходится делать это на уровне программного кода
Множество multi-get запросов для получения данных на другом конце ребер графа
Ограниченная модель данных, неэффективен для маленьких объектов
Доработки:
Порт на 64-битную архитектуру
Более эффективная сериализация
Многопоточность
Улучшенный протокол
Компрессия
Проксирование запросов
Доступ к memcache через UDP:
уменьшает расход памяти благодаря отсутствию тысяч буферов TCP соединений
управление ходом исполнения приложение (оптимизация для multi-get)
Статистика о работе потоков по запросу — уменьшает блокировки
Ряд изменений в ядре Linux для оптимизации работы memcache:
распределение управления сетевыми прерывания по всем ядрам
оппортунистический опрос сетевых интерфейсов
После вышеперечисленных модификаций memcached способен выполнять до 250 тысяч операций в секунду, по сравнению со стандартными 30-40 тысячами без данных изменений
Thrift
Что это?
Легкий механизм построения приложений с использованием нескольких языков программирования
Высокая цель: предоставить механизм прозрачного взаимодействия между языками программирования.
Предоставляет язык описания интерфейсов, статический генератор кода
Поддерживаемые языки: C++, PHP, Python, Java, Ruby, Erlang, Perl, Haskell и многие другие
Транспорты: простой интерфейс для ввода-вывода (сокеты, файлы, буферы в памяти)
Протоколы: стандарты сериализации (бинарный, JSON)
Серверы: неблокирующие, асинхронные, как однопоточные, так и многопоточные
Почему именно Thrift?
Альтернативные технологии: SOAP, CORBA, COM, Pillar, Protocol Buffers — но у всех есть свои существенные недостатки, что вынудило Facebook создать свою технологию
Он быстрый, очень быстрый
Меньше рабочего времени тратится каждым разработчиком на сетевые интерфейсы и протоколы
Разделение труда: работа над высокопроизводительными серверами ведется отдельно от работы над приложениями
Общий инструментарий, знакомый всем разработчикам
Scribe
Что это?
Масштабированный распределенный механизм ведения логов
Перемещает данные с серверов в центральный репозиторий
Широкая сфера применения:
Логи поисковых запросов
Публикации в новостных лентах
Данные по A/B тестированиям
Более надежен, чем традиционные системы логгирования, но недостаточно надежен для транзакций баз данных
Простая модель данных
Построен на основе Thrift
Хранение фотографий
Сначала сделали это просто:
Загрузка на сервер: приложение принимает изображение, создает миниатюры в нужных разрешениях, сохраняет в NFS
Загрузка с сервера: изображения отдаются из NFS через HTTP
NFS построена на коммерческих продуктах
Это было необходимо, чтобы сначала проверить, что продукт востребован пользователями и они правда будут активно загружать фотографии
На самом деле оказалось, что:
Файловые системы непригодны для работы с большим количеством небольших файлов
Метаданные не помещаются в оперативную память, что приводит к дополнительным обращениям к дисковой подсистеме
Ограничивающим фактором является ввод-вывод, а не плотность хранения
Потом начали оптимизировать:
Кэширование более часто используемых миниатюр изображений в памяти на оригинальных серверах для масштабируемости, надежности и производительности
Распределение их по CDN для уменьшения сетевых задержек
Возможно сделать еще лучше:
Хранение изображений в больших бинарных файлах (blob)
Сервис, отвечающий за фотографии имеет информацию о том, в каком файле и с каким отступом от начала расположена каждая фотография (по ее идентификатору)
Этот сервис в Facebook называется Haystack и он оказался в 10 раз эффективнее «простого» подхода и в 3 раза эффективнее «оптимизированного»
Другие сервисы
SMC: консоль управления сервисами — централизованная конфигурация, определение на какой физической машине работает логический сервис
ODS: инструмент для визуализации изменений любых статистических данных, имеющихся в системе; удобен для мониторинга и оповещений
Gatekeeper: разделение развертывания и запуска, A/B тестирования, таргетированный запуск, постепенный запуск
И еще около 50 других сервисов...
Как это работает все вместе?
Новые альбомы друзей
Получаем профиль по идентификатору пользователя (скорее всего из кэша, но потенциально возможно обращение к базе данных)
Получаем список друзей (опять же на основе идентификатора пользователя из кэша или из базы данных в случае промаха)
Параллельно запрашиваем идентификаторы последних 10 альбомов для каждого из друзей (multi-get, каждый промах мимо кэша индивидуально вытаскивается из MySQL)
Параллельно получаем данные о всех альбомах (на основе идентификаторов альбомов из предыдущего шага)
Все данные получены, выполняем логику отрисовки конкретной страницы на PHP
Отправляем HTML в браузер, пользователь счастлив
Новостная лента
Поиск
Подводим итоги
LAMP не идеален
PHP+MySQL+Memcache решает большинство задач, но не может решить совсем все:
PHP не может хранить состояния
PHP не самый производительный язык
Все данные находятся удаленно
Facebook разрабатывает собственные внутренние сервисы, чтобы:
Располагать исполняемый код ближе к данным
Скомпилированное окружение более эффективно
Некоторая функциональность присутствует только в других языках программирования
Философия сервисов:
Создание сервисов только при необходимости (минимизация издержек по развертке, поддержке и ведению отдельной кодовой базы; потенциальная дополнительная точка сбоя)
Создание общего набора инструментов для создания сервисов (Thrift, Scribe, ODS, средства мониторинга и уведомлений)
Использование правильных языка программирования, библиотек и инструментов для решения задачи
Возвращение инноваций общественности — важный аспект разработки в Facebook:
Опубликованные свои проекты:
Thrift
Scribe
Tornado
Cassandra
Varnish
Hive
xhprof
Доработки популярных решений:
PHP
MySQL
memcached
Информация о взаимодействии Facebook с opensource-сообществом, этих и других проектах расположена на странице, посвященной opensource.
Ключевые моменты культуры разработки в Facebook:
Двигайся быстро и не бойся ломать некоторые вещи
Большое влияние маленьких команд
Будь откровенным и инновационным