Спроси backend-разработчиков Badoo. Часть 1. Платформа
Нам очень нравится формат AMA (ask me anything) на Reddit, когда кто-нибудь (в нашем случае — команда разработчиков) приходит в сабреддит AMA и говорит, что готов отвечать на заданные вопросы. Из самых запоминающихся сессий Ask Me Anything, например, команда инженеров Space X, или инженеры из Google, и даже действующий президент США Барак Обама четыре года назад отвечал на вопросы на Реддите. Недавно наша Android-команда проводила AMA и в онлайн-режиме отвечала на вопросы разработчиков.
Но в России нет своего Реддита. Зато есть свой Хабр. Поэтому мы решили прийти с форматом «задай нам вопрос» сюда. И не с пустыми руками, как велят правила AMA. Чтобы вам было проще понять тему, мы выбрали одну из наших команд — «Платформу» — и попросили ребят рассказать, чем они занимаются, на чём программируют и чего добились за время существования команды. И подвели небольшие итоги уходящего 2016 года. Поехали!
Оглавление
1. Чем занимается «Платформа»
2. Сервисы: Pinba, SoftMocks и другие
3. Системное программирование. Как мы начали использовать Go и к чему это привело
4. Фотографии
5. Скриптовое облако
6. LSD: Live Streaming Daemon
7. Cassandra Time Series: что это и как работает
8. Badoo AMA: задай вопрос разработчикам «Платформы»
Пруф, что это действительно мы.
Чем занимается «Платформа»
Антон Поваров, einstein_man, руководитель «Платформы»
Михаил Курмаев, demi_urg, руководитель команды A-Team
«Платформа» — это инфраструктурная команда, которая помогает другим подразделениям. Наша основная цель — сделать так, чтобы всем было хорошо, чтобы всё работало, программисты были довольны и могли спокойно писать код без оглядки на всякие сложные штуки. Мы — backend для backend.
«Платформа» состоит из двух команд: команда С-программистов (пишут на С, но в последнее время — и на Go) и А-Team (пишут на PHP и местами тоже на Go). Сишники пишут сервисы, делают PHP extensions. A-Team занимается PHP и database-инфраструктурой, а также разработкой и поддержкой инструментов для других команд.
Если говорить о конкретных проектах с точки зрения того, что видит пользователь (то, что он использует), то мы занимаемся:
- фотографиями: вся инфраструктура хранения и отдачи лежит на нас;
- у нас есть своё скриптовое облако (это «распределённый крон», который позволяет нам запускать офлайн-обработчики на облаке машин);
- отвечаем за все «обертки» к сервисам (предоставляем остальным командам Badoo удобные доступы к сервисам, потому что у нас есть шардинг, есть самописные способы репликации).
За «обертки» мы отвечаем, потому что хочется скрыть все эти внутренности от backend-разработчиков других команд, чтобы упростить их работу и не допускать непредвиденных ситуаций, когда всё, чем мы занимаемся из основных задач, внезапно сломалось.
Сервисы: Pinba, SoftMocks и другие
Некоторые из наших внутренних сервисов со временем выросли в полноценные продукты и даже стали стандартом де-факто в экосистеме PHP. Самые известные из них — это PHP-FPM (сейчас он стал частью стандартной поставки PHP для веба), его написал Андрей Нигматулин (доклад на эту тему можно посмотреть здесь), и Pinba (http://pinba.org/), сервис для получения realtime-статистики от работающих приложений без накладных расходов на её сбор (UDP), с помощью которого легко понимать, что происходит с производительностью вашего приложения в любой момент времени.
Pinba удобна тем, что позволяет нам всё время собирать данные. И эти данные будут под рукой всегда, когда нужно разобраться в причине проблемы. Это удобно и значительно сокращает время, затрачиваемое на поиск и устранение проблемы. Не менее важно и то, что Pinba помогает увидеть проблему заранее, когда она ещё не затронула юзеров.
Ещё мы придумали и сделали SoftMocks, это наш собственный фреймворк, облегчающий unit-тестирование, который позволяет подменять классы и функции в тестах. Нам пришлось создать его в связи с переходом на PHP7, в котором была сильно переработана внутренняя архитектура интерпретатора, и наш старый Runkit просто перестал работать. На тот момент у нас было около 50к юнит-тестов, большинство из которых так или иначе используют моки для изоляции внешнего окружения, и мы решили попробовать другой подход, потенциально более мощный, чем Runkit/ Uopz.
Одними из основных преимуществ SoftMocks являются независимость от внутренней структуры интерпретатора и отсутствие необходимости в каких-либо сторонних расширениях PHP. Это достигается за счёт выбранного нами подхода — переписывание исходного кода программы на лету, а не динамическая подмена внутри интерпретатора. На данный момент проект выложен в open-source, и им может воспользоваться любой желающий.
Вы, возможно, знаете о том, у нас в Badoo очень сильная команда PHP-разработчиков. Поэтому нет ничего удивительного в том, что мы оказались в числе первых компаний, которые перевели проект такого масштаба (как Badoo) на PHP 7 в этом, 2016-м, году. Прочитать о том, как мы пришли к этому, с чем столкнулись и что получили, можно в этом посте.
Системное программирование. Как мы начали использовать Go и к чему это привело
Марко Кевац, mkevac, программист в отделе C/C++
В отделе C/C++ мы разрабатываем высокопроизводительные in-memory демоны, которые обрабатывают сотни тысяч запросов в секунду и хранят сотни гигабайт данных в памяти. Среди них можно найти такие вещи, как поисковые демоны, использующие bitmap-индексы и осуществляющие поиск по ним с использованием самописного JIT, или умный прокси, который обрабатывает соединения и запросы всех наших мобильных клиентов. При необходимости мы расширяем язык PHP под наши нужды. Какие-то патчи отправляются в апстрим, какие-то — слишком специфичны для нас, а какие-то вещи удаётся сделать в виде загружаемых модулей. Мы пишем и поддерживаем модули для NGINX, занимающиеся такими вещами, как шифрование урлов и данных и быстрая обработка фотографий «на лету».
Мы — хардкорные системные программисты, но при этом прекрасно понимаем все недостатки программирования на С/C++: медленная разработка, потенциальные ошибки, сложность программирования с использованием потоков.
С самого появления Go, новомодного, молодёжного и многообещающего языка от Google, мы им заинтересовались. И почти сразу же после выхода первой стабильной версии в 2012 году мы начали рассматривать возможность его применения в production.
Go обещал быть близким по духу и производительности нашему любимому С, но позволял делать прототипы и даже конечные продукты заметно быстрее и с меньшим количеством ошибок. И тот факт, что Go являлся синонимом конкурентности с его каналами и горутинами, особенно возбуждал нашу фантазию.
В тот момент у нас появилась новая крутая и очень срочная задача по поиску пересечений между людьми в реальном мире. Послушав требования, мы чуть ли не хором воскликнули: «Это задача для Go!» Требовалось потоково обработать большое количество координат пользователей, правильно их пересечь в нескольких «координатах», включая время, и выдать какой-то результат. Очень много взаимодействий между частями, очень много параллельных вычислений. Словом, именно то, что является базовой задачей для Go.
Прототип был сделан тремя людьми за неделю. Он работал. Он работал хорошо. И мы поняли, что Go у нас приживётся. В 2015 году Антон Поваров подробно рассказал в своём выступлении о Go в Badoo.
Но нельзя сказать, что наш роман был идеальным. Go на тот момент был совсем молодым языком с кучей проблем, а мы сразу же начали писать продукты, которые обрабатывали десятки тысяч запросов в секунду и потребляли почти 100 гигабайт памяти.
Нам приходилось оптимизировать наши сервисы, чтобы не делать лишних аллокаций памяти напрямую и чтобы компилятор Go не решал делать эти аллокации за нас. И здесь красота и удобство Go снова проявили себя. С самого начала Go обладал отменными средствами для профилирования производительности, потребления памяти, для того, чтобы видеть, когда компилятор решает выделить какой-то кусок на куче, а не на стеке. Наличие этих средств сделало оптимизацию интересным и познавательным приключением, а не мукой.
В первом же проекте нам нужно было использовать существующую библиотеку для геовычислений, написанную на C. Так что мы окунулись в самую гущу проблем и нюансов взаимодействия этих двух языков.
Поскольку Go стал инициативой «снизу», нам нужно было постараться, чтобы наши коллеги и менеджеры не отвергли нашу идею сразу. Мы понимали, что нужно сделать так, чтобы со стороны эксплуатации проект на Go никак не отличался от проекта на C: такие же конфиги в JSON, те же протоколы взаимодействия (основной protobuf и дополнительный на JSON), та же стандартная статистика, которая уходит в RRD. Нам надо было сделать так, чтобы со стороны релиз инжиниринга проекта на Go никак не отличался от проекта на C: тот же самый Git + TeamCity Flow, та же сборка в TeamCity, тот же процесс выкладки. И у нас это получилось.
Администраторы, эксплуатация и релиз-инженеры не задумываются о том, на чём написан проект. Мы поняли, что теперь можно не стесняться использовать новые инструменты, так как они прекрасно показали себя на практике (в некритичных задачах, как и положено для начала).
Мы не создавали ничего с нуля — мы встраивали Go в существующую много лет инфраструктуру. Этот факт ограничивал нас в использовании каких-то вещей, которые для Go являются стандартными. Но именно этот факт вкупе с тем, что мы сразу начали писать серьёзный высоконагруженный проект, позволил нам окунуться в язык по уши. Мы знатно испачкались, скажу я вам, но эта близость помогла нам «срастись» с этим прекрасным языком.
Интересно было наблюдать, как с каждой выходящей версией Go рос, как ребёнок, превращающийся во взрослого человека. Мы видели, как паузы GC на наших демонах таяли с каждой новой версией, и это без изменения кода с нашей стороны!
Сейчас, спустя четыре года работы с этим языком, у нас около десятка самых разноплановых сервисов на Go в трёх командах и ещё несколько новых в планах. Go прочно вошёл в наш арсенал. Мы знаем, как его «готовить» и когда его стоит применять. Спустя столько лет интересно слышать, как программистами регулярно говорятся такие вещи, как «да набросай быстренько прототип на Go» или «тут столько параллельности и взаимодействий, это работа для Go».
Фотографии
Артём Денисов, bo0rsh201, старший PHP-программист
Фотографии являются одним из ключевых компонентов Badoo с точки зрения продукта, и мы просто обязаны уделять инфраструктуре их хранения и показа много внимания. На данный момент мы храним около 3 ПБ фотографий, каждый день пользователи заливают около 3,5 млн новых снимков, а нагрузка на чтение составляет около 80k req/sec на каждой площадке.
Концептуально это устроено следующим образом. У нас есть три точки присутствия в трёх дата-центрах (в Майами, Праге и Гонконге), которые обеспечивают локальность к большинству наших целевых рынков.
Первый слой инфраструктуры — это кэширующие серверы с быстрыми SSD дисками, которые обрабатывают 98% входящего трафика, на них работает наш собственный мини-CDN — это кэширующий прокси, оптимизированный под наш характер нагрузки, на котором также работает много утилитарной/ продуктовой логики (ACL, resize, наложение фильтров и watermark«ов на лету, circuit breaker и т. д.).
Следующий слой — кластер из пар серверов, ответственных за долговременное хранение,
часть из которых имеет локальные диски, на которых непосредственно хранятся фотографии, а часть подключена по оптике к третьему слою — Storage Area Network.
Эти пары машин обслуживают одинаковые диапазоны пользователей и работают в режиме master — master, полностью реплицируя и резервируя друг друга через асинхронную очередь. Наличие таких пар позволяет нам иметь отказоустойчивость не только на уровне жёстких дисков, но и на уровне физических хостов (kernel panic, reboot, blackout и т. д.), а также легко проводить плановые работы и без деградации сервиса переживать сбои, которые при больших масштабах не являются редкостью.
Более подробно о нашей работе с фотографиями Артём Денисов рассказал в этом году на Highload++.
Скриптовое облако
Ни для кого не секрет, что в любом проекте, помимо действий, которые выполняются в контексте запроса пользователя, существует большое количество фоновых задач, выполняемых отложенно или по определённому расписанию. Обычно для их запуска применяют какой-то планировщик background worker«ов (в простейшем случае это cron).
С ростом количества таких задач и количества потребляемых ими ресурсов, которые постепенно перестают влезать на одну, а иногда и на несколько десятков физических машин, управлять этими кронами и балансировать нагрузку вручную для каждой ноды из кластера становится сложновато. Так возникла необходимость создания нашего облака — гибкой инфраструктуры для прозрачного запуска девелоперских задач.
Работает это примерно следующим образом:
1) Разработчик описывает job в виде PHP-класса, который реализует один из нескольких интерфейсов (крон-скрипт, разборщик очереди, обходчик баз и т.д…
2) Добавляет его через веб-интерфейс в облако, выбирает параметры для частоты запуска, тайм-ауты и ограничения по ресурсам.
3) Далее система сама запускает этот job на распределённой инфраструктуре, которая выделена под облако, следит за его выполнением и балансирует нагрузку на кластер. Девелоперу остаётся лишь следить за статусом работы своего job«а и смотреть логи через Web UI (сколько инстансов запущено, какие настройки, какие запуски как завершились).
На данный момент в облаке у нас порядка 2000 хостов в двух ДЦ ~ 48k CPU cores/ 84Tb memory. 1800 пользовательских заданий генерируют около 4 000 запусков в секунду.
Об облаке мы рассказывали тут и тут.
LSD: Live Streaming Daemon
Каждый, кто работает с большими объёмами данных, так или иначе сталкивается с задачей их стриминга. Как правило, мы стримим какие-то данные из большого количества разных источников в одно место, чтобы там централизованно их обработать. Причём тип этих данных зачастую не играет роли: мы стримим логи приложений, статистические данные, пользовательские события и многое другое. Концептуально мы используем два разных подхода для решения этой задачи:
1) Нашу собственную реализацию сервера очередей для доставки событий, связанных с логикой продукта/ приложения.
2) Более простой механизм для стриминга различных логов, статистических метрик и просто больших объёмов данных со множества нод, которые надо централизованно агрегировать и обрабатывать большими пачками в одном месте.
Для второй задачи мы долгое время использовали Scribe от Facebook, но с увеличением количества прокачиваемых через него данных он становился всё менее и менее предсказуемым и уже давно предан забвению.
В итоге в какой-то момент нам стало выгоднее написать своё решение (благо эта задача не выглядит очень сложной), которое было бы легче поддерживать.
Собственную стримилку событий мы назвали LSD: Live Streaming Daemon.
Ключевые особенности LSD:
- транспорт в виде строк из plain файлов (для клиента нет ничего надёжнее, чем запись данных в файл локальной FS);
- клиенты не блокируются при записи, даже если все серверы-приёмники недоступны (буфер скапливается в локальной FS);
- прозрачный контроль/ настройка лимитов на потребление сетевых/ дисковых ресурсов;
- совместимый со scribe формат записи/ способ агрегации файлов на приёмнике.
В этом году мы опубликовали исходный код LSD, и теперь вы можете использовать его в своих проектах.
Cassandra Time Series: что это и как работает
Евгений Гугучкин, che, старший PHP-программист
Badoo — сложная система, состоящая из множества связанных компонентов. Оценить состояние этой системы — непростая задача. Для того чтобы это сделать, мы собираем более 250 миллионов метрик со скоростью около 200 000 значений в секунду, и эти данные занимают примерно 10 ТБ.
Исторически для хранения и визуализации временных рядов мы использовали известную утилиту RRDtool, «обернувши» её своим фреймфорком для удобства работы.
Что нам нравилось в RRDtool, так это скорость чтения. Однако существуют и серьёзные недостатки:
- высокая нагрузка на диски, порождаемая большим количеством операций ввода/ вывода с произвольным доступом (эту проблему мы решили использованием SSD и RRDcached);
- отсутствие возможности записи задним числом: то есть если мы записали значение на момент времени 2016–01–01 00:00:00, то записать следом значение за 2015–12–31 23:59:59 уже не получится);
- довольно расточительное использование места на диске для разреженных данных;
- доступ к данным осуществляется локально: невозможно из коробки построить распределённую систему с горизонтальным масштабированием.
Именно последний пункт стал для нас решающим, потому что без него мы не могли отобразить на одном графике метрики с разных серверов.
В итоге мы провели подробнейший анализ существующих решений в области time series баз данных, убедились, что ни одно нам не подходит, и написали своё решение на основе Cassandra.
На данный момент половина наших реальных данных дублируется в новое хранилище. В цифрах это выглядит так:
- девять серверов;
- 10 Тб данных;
- 100 000 значений в секунду;
- 140 миллионов метрик.
При этом мы решили практически все задачи, которые перед нами стояли:
- выход из строя одной ноды в кластере не блокирует ни чтение, ни запись;
- есть возможность дописывать, а также переписывать «сырые» данные, если они не старше недели (окно, в котором можно менять данные, можно настраивать и менять в процессе эксплуатации);
- простое масштабирование кластера;
- нет необходимости использовать дорогие SSD диски;
- более того, можно не использовать RAID-массивы из HDD с резервированием, поскольку при замене диска нода способна восстанавливать данные с соседних реплик.
Мы очень гордимся проделанной работой по анализу существующих решений и построению нового. Мы наступили на бесчисленное количество грабель при работе с Cassandra и с удовольствием ответим на ваши вопросы, поделимся нашим опытом.
Badoo AMA: задай вопрос разработчикам «Платформы»
И теперь, собственно, то, зачем мы публикуем этот пост. Сегодня с 12:00 и до 19:00 (по московскому времени), команда «Платформы», будем отвечать на ваши вопросы. Мы много всего пережили за время существования команды: мы расширялись, менялись, учились, сталкиваясь с какими-то проблемами, приходили к новым языкам программирования. И мы готовы поделиться с вами нашим опытом (в том числе рассказать про фейлы, факапы и нашу боль).
Например, спрашивайте про:
- устройство наших внутренних проектов (как устроен наш шардинг, как мы собираем и рисуем статистику, как общаемся с сервисами);
- то, на какие грабли мы наступали, почему принимали те или иные решения (что мы делали, когда перестали умещаться на одном сервере, в одном ДЦ);
- настройку работы сильной команды PHP/ С-программистов;
- переход на PHP 7 (с какими проблемами можно столкнуться);
- особенности работы с высоконагруженным проектом;
- рекомендации PHP- и Go-программистам;
- всё, что описано выше в посте.
Но не ограничивайтесь этим!