Наверх ▲

Open Source разработка в Badoo

Алексей Рыбак Алексей Рыбак Ex-глава разработки Badoo.
View more presentations from HighLoad2009.

Алексей Рыбак: Меня зовут Алексей Рыбак, я представляю компанию Badoo. Сегодня я постараюсь рассказать вам о наших разработках с открытым исходным кодом (англ. open source).

Во многих компаниях, которые серьезно занимаются поддержкой своего кода, есть какие-то внутренние наработки, которые которые время от времени выходят в виде продуктов Open Source. Мне самому было бы интересно послушать, что использует и предлагает разработчикам какая-нибудь крупная компания. Мне было бы интересно знать, есть ли у этой компании какие-то готовые полезные продукты, которые я могу использовать в своем проекте? Поэтому я расскажу о некоторых общедоступных вещах, которые сделали за последнее время мы. Вкратце расскажу о том, что такое Badoo, расскажу о PHP-FPM – это FastCGI-менеджер.

PHP-FPM – это FastCGI процесс-менеджер, который только недавно начал набирать популярность. Я немного расскажу о том, для чего он нужен. Совсем недавно, мы открыли проект, который называется Pinba. Это сервис статистики для PHP-приложений, который я активно использую для мониторинга и исследования производительности наших приложений. Еще буквально несколько слов я скажу о нашем шаблонном движке.

Сначала я просто расскажу, что такое Badoo. Badoo – это нечто среднее между социальной сетью и сайтом знакомств (англ. dating site). Это достаточно большой проект, не очень известный в России и рассчитанный, в основном, на западную аудиторию. Исторически получилось так, что люди, которые создали Badoo, до этого делали "Мамбу". Можно сказать, что Badoo - это та же "Мамба", но на западном рынке. При этом у нас много разного функционала соцсетей типа загрузки фоток, медиа и блогов... В сентябре было где-то 47 миллионов пользователей, сайт поддерживает 17 языков. Это мультиязычный сайт, потом я расскажу об этом чуть-чуть подробнее. Мы в нашей работе используем LNMP stack – это «N» заменило «А», потому что используется nginx. Ну, Linux, MySQL, РНР… Есть отдельная команда, которая пишет на "Си" или "Си++", и почти вся разработка, которая у нас ведется и о которой я расскажу сегодня, – это что-то на "Си" вокруг РНР.

Важно понимать следующую вещь: мы – маленькая компания, действительно маленькая для такого проекта, и для нас основной ценностью является удобство в эксплуатации.

Голос с места: Сколько? Какое число?

Алексей Рыбак: Какое число, простите, чего?

Голос с места: Разработчиков. Сколько их в команде у вас?

Алексей Рыбак: Размер команды, если сейчас взять всех разработчиков, всех менеджеров и всех, как-то относящихся к проекту, то будет человек 25.

Голос с места: А по пропорции? То есть, менеджеры/разработчики?

Алексей Рыбак: Какое-то разумное количество, ну, не один к одному, вы же понимаете. Большинство – разработчики. И поэтому так получилось, что все наши разработки, так или иначе направлены на то, чтобы сделать наш проект более управляемым, надежным, производительным и так далее.

Итак, первая вещь, о которой я хочу рассказать, – это PHP-FPM. PHP-FPM – это менеджер FastCGI-процессов для PHP. Он немного отличается от того, что идет в стандартной поставке РНР. Он, скажем так, немного "умнее" того, что есть у РНР. Архитектура этого сервера напоминает Nginx – есть некий мастер-процесс, некоторое количество обработчиков (англ. workers), все это подается каким-то файлом конфигурации. Образно говоря, "папа пасет детей" и очень аккуратно реагирует на проблемы, которые происходят. Несколько более аккуратно, чем то, что мы берем в стандартной поставке РНР.

Прежде, чем мы будем говорить о PHP-FPM, я бы хотел объяснить, почему заранее предупредил об эксплуатации. Давайте представим себе, что у нас есть кластер веб-серверов, на котором "живут" веб-приложения. Что для нас было бы важно? Во-первых, для нас было бы важно иметь возможность обновлять бинарник РНР, включая всякие разные расширения (англ. extension), не теряя соединений и не "положив" сервера. Эти две вещи стандартная поставка не обеспечивает. Это мы сделать стандартным не можем.

Кроме того, существует большое количество разных сторонних библиотек (англ. third-party libraries), которые могут «уронить» нашу "детку" - там может произойти ошибка, и из стандартной поставки мы об этом никак не узнаем. И это не очень приятно, потому что не очень понятно, что в таких случаях делать. Мы даже не знаем, что произошло. Для службы поддержки очень важно хотя бы видеть, в чем была проблема, если проблема уже произошла.

И, наконец, было бы здорово автоматически реагировать на разного рода стандартные типы "поведения" обработчиков в случае неприятных ситуаций. Часто, например, происходило следующее… Как вы знаете, в РНР изначально было очень много разных менеджеров байт-кода. И так получилось, что только в версии 6 один из менеджеров байт-кода, акселераторов, войдет в поставку. Ранее это пытались делать отдельные ребята вроде Zend. Все это более-менее стабильно работало, но иной раз происходила какая-то проблема с памятью в этом акселераторе, байт-код "бился", и все "детки" просто "умирали". Поскольку такое происходило часто, то было бы здорово, если бы наше ПО на такие штуки как-то реагировало.

Эти задачи и предопределили возможности PHP-FPM, которые, в первую очередь, были обусловлены удобством эксплуатации. Во-первых, мы хотим иметь возможность плавно перезапускаться и обновлять наш код. Во-вторых, в PHP-FPM мастер внимательно "ловит" stderr всех "детей", складывает их в отдельное место, и таким образом мы получаем единый log-файл всех проблем. В-третьих, есть специальная опция для автоматического отслеживания и завершения работы медленных обработчико. В том случае, если какой-то обработчик начинает тормозить, есть два лимита: один из лимитов – это просто медленно работаем, попробуем отследить, что было не так. Если все совсем медленно, "гасим" этот обработчик и запускаем вместо него новый. Наконец, если у нас "побилась" общая память и все "детки" начали массово "умирать", "папа" это может каким-то образом обнаружить и массово перезапустить "детей", тем самым избежав аварии и вмешательства системного администратора.

Помимо этого, в PHP-FPM есть ряд дополнительных возможностей, и некоторые из них довольно интересны. Во-первых, в течение долгого времени у PHP был один неприятный недостаток. В случае, если происходила ошибка (англ. fatal error), fastcgi все равно получал 200 ОК, белую страницу. На многих проектах это есть до сих пор, а PHP-FPM на этот случай просто имеет определенную директиву – на что подменить, какой заголовок использовать (у нас это называется "error header"), и в таком случае правильно настроенный веб-сервер будет отдавать правильный контент.

Во-вторых, существует такая возможность, как завершение запроса (англ. FastCGI finish request). Что это такое? Дело в том, что когда PHP-FPM собирает страницу, часто бывает нужно что-то записать уже после того, как примерно понятно, какой контент будет отдан пользователю. Например, это может быть какая-то запись "output". Запишем ли мы это до того как отдали данные пользователю, или после, на данные, непосредственно на эту страницу, это влиять не будет. Поэтому для того, чтобы пользователь ждал меньше, мы можем сделать следующий трюк. Мы можем в какой-то момент сказать, что мы страничку собрали, все отдали, даем специальную команду о том, что мы отдаем весь контент веб-серверу, но при этом еще продолжаем работать. Продолжаем работать, какое-то время пишем что-то на диск или сохраняем что-то куда-то, и после этого, когда "детка" полностью закончит работу, "детка" начинает обрабатывать следующий запрос. Таким образом у нас работает запись в memcached для сессий - это запись в некий специальный сервис, который требует посещения пользователей, в общем, это самые стандартные, паттерные использования этой функции.

И, наконец, одна из специальных возможностей PHP-FPM – это ускорение загрузки на сайт (англ. accelereted upload). Если веб-сервер, например, nginx, поддерживает "request body file", значит, тоже FPM поддерживает это и с меньшим использованием ресурсов "пробрасывает" большие посты в ваше приложение.

Мне хотелось бы отдельно сказать о том, что из себя сейчас представляет FPM - что использовать, зачем все это нужно. Вообще, FPM как конечный продукт сформировался лишь в 2007 году. Мы начали его в 2004-м, и тогда это был набор разрозненных патчей. По-моему, это была еще Мамба, и где-то до 2006 года это просуществовало в таком виде. После этого появился один большой патч поверх РНР. Почему был один большой патч поверх РНР? Потому что, во-первых, он в себя включал патченный libevent, так как использовать стандартный libevent на тот момент разработчик не хотел из-за ошибок -  он пытался связаться с командой разработки libevent, но они отчет об ошибках сразу не приняли. В общем, это все отнимало какое-то время. И патч PHP был написан, соответственно, по такой же причине: времени на общение относительно того, почему PHP сделан так, а мы хотим сделать так, особо не было, поэтому какое-то время проект существовал просто как отдельный патч на РНР. Потом оказалось, что все работает достаточно стабильно, это версия – ветка 0,5, вы можете даже про нее почитать на старом сайте php-fpm.org. Проект стал пользоваться достаточно большой популярностью, стало приходить много людей, которые стали задавать простые вопросы, нужно было с сообществом как-то работать… В общем, оказалось, что у разработчика нет достаточных ресурсов и желания, чтобы это сообщество развивать. Потом появились какие-то люди, которые сказали, во-первых, что они готовы этим заниматься, а во-вторых, что они готовы разговаривать с командой PHP на предмет того, как сделать так, чтобы PHP все-таки стал включать в себя этот FPM. Потому что большое количество РНР-проектов нагружено, они уже давно его используют.

На данный момент есть ветка 0,6, ее поддерживает уже другая команда, которой руководит человек по имени Майкл Шедл (Michael Shaddle). На groups highload-php-(en|ru) и на googlegroups.com есть соответствующие рассылки. Мы до сих пор не знаем, когда PHP войдет в FPM, но я верю в эту команду и надеюсь, что рано или поздно они этого добьются. (Комментарий: эта информация уже устарела – FPM ещё в конце 2009 года был добавлен в SVN репозиторий PHP).

На этом про FPM все, и я хочу рассказать о нашем иструменте для сбора статистики - Pinba. Расшифровывается это как «PHP is Not Bottleneck Anymore». В том или ином виде Pinba иcпользуется, начиная, наверное, с 2005 года. По-моему, только в этом или в прошлом году появился сайт www.pinba.org, и мы выложили туда исходники этого приложения. Кстати, слово «pinba» также означает какого-то там попугая на одном из диалектов австралийских аборигенов, поэтому на сайте вы будете лицезреть этого попугая.

Давайте вернемся к эксплуатации. Вот мы занимаемся эксплуатацией веб-кластера. Какие статистические, чисто технические данные, нам было бы интересно знать? Во-первых, было бы интересно знать общее число запросов на сервер. Я имею в виду динамический запрос, который "пробрасывается" на ПК на PHP. Было бы интересно знать время ответа от РНР. Хотелось бы, конечно, знать среднее по позиции. Хотелось бы знать статистику по скриптам. Всегда есть тяжелые какие-то скрипты, есть более легкие. Если вы используете несколько различных хостов, возможно, вы захотите знать статистику для различных хостов. Итак, если у вас при этом есть разные машины… Одна поставка была в одном году, там используются менее мощные машины, потом вы закупили более мощные серверы, каким-то образом раскидываете трафик между ними и хотите знать, насколько эффективно вы это делаете. Поэтому вы, наверное, хотели бы знать относительно физических машин: справляются они или не справляются. Может быть, стоит дать побольше нагрузки на этот кластер или на тот. Наконец, было бы интересно знать все про использование ресурсов. Сколько времени мы проводим в ожидании? Это может быть база данных или диск, сервис какой-то, memcache – сколько мы истратили системного или процессорного времени. Понятно, что по большому счету все, что вам нужно, - все эти данные превратить в цифры, обладая непрерывным мониторингом в режиме реального времени… Имея такой мониторинг, вы можете, во-первых, ввести определенный уровень и качество. Можете использовать триггер, который сработает, если, например, время ответа с какого-то сервера или просто в среднем по системе у вас превысило 0,5 секунды, а вы считаете, что больше чем 0,5 секунды – это плохо, потому что пользователи начнут уходить с сайта. И, наконец, вы таким образом можете просто тестировать качество приложений, смотреть в реальном времени после релиза, что же у вас меняется.

Как устроен сервис Pinba? Он устроен достаточно хитро. Во-первых, это клиентский модуль для РНР, который на момент начала запроса начинает считать. На момент окончания Pinba прекращает считать, получает какую-то информацию и потом этот UDP-пакет отправляет на сервер, который прописан у него в конфигурационном файле. На сервере крутится MySQL (выше версии 5, иначе работать не будет). Есть специальная подключаемая подсистема управления хранением данных (англ. pluggable storage engine) для MySQL, которая называется Pinba. И в этой подсистеме управления хранением есть специальный серверный поток, который собирает все эти UDP-пакеты и предоставляет SQL-интерфейс ко всем данным, которые вы хотите получить. Этот SQL-интерфейс предоставляется к "сырым" данным, то есть просто по каждому запросу, но это работает медленно, и поэтому он предоставляет SQL-интерфейс к специальным отчетам, которые генерируются "на лету". Прежде чем мы перейдем к отчетам, я немного расскажу об этих данных.

Для каждого запроса на сервер уходят название скрипта, хост, домен, время, которое мы потратили, полное системное время (rusage), и так далее. И еще всякие специальные таймеры. Таймеры – это отдельная, модная и очень важная фишка. Таймер – это следующая штука. Вы оборачиваете ваш код кастомно, и так мы обычно что-то меряем: мы включили таймер, выключили таймер и взяли какую-то дельту, да? В данном случае вот это включение-выключение имеет абсолютно любой набор атрибутов ключ-значений. Например, если вы делаете запрос к базе данных, в базе данных у вас используется какая-то единая обертка, и в этой единой обертке вы поставили таймер, у которого есть всего лишь два ключа – один ключ называется «группа», другой ключ называется «сервер». И в «группу» вы, допустим, ставите db, db-update, db-select и так далее, в «сервер» вы пишете хост-нейм для сервера, то каждый таймер у вас идентифицируется вот этими двумя парами ключ-значение. Эти таймеры "прицепляются" вместе с общими данными, уходят на сервер, и на сервере вы имеете возможность через SQL получить статистику по, допустим, обновлениям, на какую-то группу серверов, допустим, с сорок второго по сорок восьмой. То есть это все превращается в очень удобные запрашиваемые данные. У нас обернуто в такие таймеры практически все – запросы к базе данных, запросы к внешним сервисам, сервисам на "Си", и иногда кастомно мы еще где-то их иногда расставляем. Обычно это делается крайне редко - запросы к базе, к внешним сервисам, к memcache. Все это дело обертки.

У меня, к сожалению, нет возможности подробно рассказывать, как это выглядит применительно к SQL, поверьте, все очень просто, на сайте www.pinba.org есть много разных примеров - есть отчеты, которые можно получать из так называемых "сырых" данных, там каждая сущность с запросом, каждый таймер является просто строкой в таблице, и есть при этом возможность генерировать так называемые "отчеты на лету", через поле "comment", которые можно задавать при создании таблицы. Можно просто сказать, что это будет отчет, отчет будет по "foo", по "bar". И "foo", и "bar" у нас будут значениями таймеров, соответствующих "foo" и "bar". Таким образом, у нас получается плоская таблица, в которой уже есть заранее сгенерированный счетчик… Значение "count" – это общее число событий, которые были отмаркированы соответствующими тегами (мы называем ключ-значение тегом), общее время, а также название скрипта и "foo", и "bar", которые встретились. Таким образом, если вам нужно по скриптам, которые показывают главную страницу, посмотреть, сколько было запросов на обновление какого-то сервера, то, значит, обновление – это будет "foo", сервер – это будет "bar", и вы получите все эти цифры. Все очень удобно, просто и на лету считается.

Как-то раз у нас произошла авария. В какой-то момент были серьезные всплески, время ответов составляло несколько секунд, авария действительно была серьезная, исправить все быстро не удалось. Я сейчас пытаюсь вспомнить, что это было - это было года два назад. Имел место очень серьезный релиз, и в какой-то момент произошла утечка памятив в РНР. При этом не были выставлены правильным образом лимиты на память, и машины просто начинали слабеть. Мы не сразу это обнаружили: прошла пара дней, пока мы поняли, в чем дело. Сначала исправили на сервере, потом нашли нужное место на графике (там все наглядно). Я помню, что после того как мы устранили утечку, нам показалось, что все нормально. Но мы решили дойти до конца: увеличили график в размере, увидели какую-то очень странную полосатую структуру. В общем, если бы у нас не было отчетов по скриптам, то мы, наверное, в этот момент бы сказали: «Ну, не судьба, что-то там происходит непонятное». Но если "разрезать" все это по скриптам, мы видим "шум", носит вполне периодический характер. Мы знаем, какие скрипты в какой момент начинают "скакать", и приблизительно после этого понимаем, что, скорее всего, дело в регулярно запускаемых наших скриптах базы или еще в чем-то. Имея разрез по тегам, такие вещи находятся еще быстрее, потому что после этого можно посмотреть для этого скрипта, на каких обернутых таймерах мы тормозили, и вообще понять причину очень быстро. А для нас это очень важно – в случае, если у нас авария или "тормоза", надо понять, в чем проблема и быстро ее решить.

И последнее, о чем я хочу рассказать. Вообще, в 2009 году рассказывать что-то про шаблонизаторы достаточно смешно, потому что их великое множество. Я попробую точно так же рассказать о случаях, когда наш шаблонизатор нас спас. Это тоже разработка на "Си" -модуль для РНР. Исторически мы использовали проект, который называется php_template. Он тоже делался нашим соотечественником, но в какой-то момент был заброшен. А нам он очень нравится, и мы на основе него сделали кое-что свое.

Основная фишка этого проекта заключается в том, что он принуждает к практически тотальному разделению труда между верстальщиком и программистом. При этом эта штука, поскольку она прописана на "Си", практически не потребляет ресурсы процессора, и в самом аскетичном варианте все, что там есть, это просто блок. Все, что может человек, который охотится за шаблоном, - вставить переменную или вставить блок. Блоки, естественно, можно вставлять один в другой. Самое интересное, что сейчас есть возможность сделать какие-то там цифры, но ничего этого на продакшн не использовалось. И, как ни странно, эта штука нашла себе применение в мультиязычных проектах. Я расскажу чуть подробнее.

Были у нас достаточно давние тесты производительности шаблонизаторов. Если вы ищете шаблонный движок, то их условно можно разделить на нормальный, более-менее ничего и никуда не годный. Как оказалось, blitz не самый медленный движок. При этом видно, что есть чисто нативные движки, которые тратят очень много времени (FastTemplate, Sigma). Почему возникают проблемы? Дело в том, что РНР – классный язык, он стал популярным из-за того, что так легко взять и рядом с html-кодом начать писать РНР-код, но, к сожалению, для сложных проектов это приводит к появлению спагетти-кода.

Основной дизайн сложился в Blitz, он весьма аскетичный, из-за того, что мы считаем, что в аббревиатуре MVC View – это не шаблон. Кто-то считает иначе: как только мы получили в модели все данные, дальше их передать надо во View, а View – это не что иное, как шаблон. Так что пусть он какими-то цифрами или прочими вызовами все это расставит. Нет, мы считаем, что View – это шаблон плюс некая логика, которая управляет этим шаблоном, и она должна лежать отдельно, для того чтобы верстальщик и программист не занимались одним и тем же кодом. Если это косяк верстальщика, пусть он правит шаблон, если это косяк какой-то логики, пусть программист правит РНР-код. Никакого смешения РНР-кода и html у нас не происходит. Самый главный минус: при таком раскладе движок становится не очень понятным для новичков и одиночек - им как раз неудобно такое разделение, им бы хотелось из модели получить данные и как можно быстрее их использовать в шаблоне. 

За время жизни проекта очень много людей просили добавить туда всякую логику. Она была добавлена, но в Badoo мы ее не используем. Несмотря на то, что она есть и работает в других проектах, мы в Badoo стараемся этот ящик Пандоры не открывать. Принцип работы движка напоминает XML/XSLT, только все значительно проще. У нас есть какая-то структура данных, РНР – это хэш, и на эту структуру данных просто накладывается шаблон. Если нам нужна какая-то переменная, то в этой структуре данных у нас будет скаляр. Если у нас есть какой-то список, то мы в шаблоне сделаем блок, и это будет массивом в этой структуре данных. Когда мы шаблон начнем парсить с этой структурой данных, автоматически произойдет некий маппинг между структурой данных и разметкой шаблона, и соответствующие переменные отобразятся, соответствующие блоки будут проитерированы и, вообще говоря, с помощью этого подхода можно реализовать любую логику. В этом прототипе все, что угодно, можно сделать. Какие мы нашли здесь бонусы?

Во-первых, получается такой чистый шаблон. Чистый шаблон, который не содержит вообще никакого кода. Мы используем в Badoo 17 языков, значит, у нас есть системы перевода, мы тратим очень много ресурсов на то, чтобы наша система перевода корректно работала. Мы поступаем следующим образом. Обычно первое, что приходит в голову, это использование gettext для перевода. Мы делаем не так. Мы прокатываем целый шаблон, поскольку в нем нет никакой логики, там есть только некая разметка, начало блока, конец блока, переменная… мы прогоняем весь этот шаблон через парсинг, через систему перевода, и на продакшне мы прогоняем этот шаблон еще раз, уже с использованием переведенных лексем. Таким образом, на продакшне у нас лежат полностью переведенные шаблоны, никаких динамических вызовов там, где тексты, нет, и это сделать оказалось очень просто именно потому, что шаблон очень простой. И второй момент, который нам тоже очень помог в реализации мультиязычности: пока вы не начали парсинг шаблона, вы "напихиваете" в эту самую структуру данных, которая определяет состояние шаблона, много-много всего для того блока, для другого и т.д. И вы можете очень легко это состояние перед парсингом получить. Получить полностью структуру данных, куда-то сохранить и потом ее использовать для того, чтобы генерировать специальные снимки для переводчиков. Что я имею в виду? Для переводчиков, вообще говоря, перевод имеет смысл только в контексте. Переводчику удобно либо переводить с сайта, либо зайти на страницу, на которой был сделан снимок. Сейчас поясню.

Допустим, вы заполняете форму регистрации и ошибаетесь в нескольких полях. От того, насколько корректно будет переведен текст ошибки, зависит ваша конверсия. Если перевод неверен, то пользователи не поймут, что нужно поправить. Поэтому в ситуации, когда происходит какая-то ошибка, мы делаем следующее. Конкретно для этого поста, для этого состояния всех шаблонов, сохраняем снимок (это тоже оказалось легко сделать, благодаря тому, как реализован наш движок). Через какое-то время переводчик приходит, открывает точно такую же страницу, там повторяется все то же самое. Все снимки сохранены, если при этом код не сильно менялся, то ничего не сломается, и переводчик увидит ровно ту же страницу и сможет сделать перевод прямо с сайта. Дальше это все сохраняется. Такой перевод получается значительно более качественным. Вот, наверное, и все. Спасибо вам большое. Готов ответить на ваши вопросы.

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

Вопрос из зала: Почему в PHP SQL не разработан патч/плагин, нет контроллера обработчиков? Можно ли его "прибить"? Если нет, можно подробно, почему нет?
Алексей Рыбак: Это хороший вопрос, почему он не разработан. Он был действительно заявлен. Дело в том, что нам, например, он совершенно не нужен. Мне сложно представить ситуацию, когда нужно динамически плодить большое количество обработчиков и потом их также динамически "прибивать". Я готов поверить, что эти ситуации есть, но, видимо, мнение людей, которых это очень беспокоит, и им это нужно, не было учтено. Получится или не получится, нужно спрашивать у команды людей, которая сейчас этим занимается. У нас сейчас имеется достаточно большое число обработчиков на машине, они неизменны, и эта схема прекрасно работает. (Комментарий: Этот функционал был недавно добавлен – сделал это Jérôme Loyet.)
Вопрос из зала: Я так понял, собирается статистика по использованию ресурсов на каждый конкретный скрипт, но при этом таймеры собирают информацию только о времени выполнения. Есть ли собирать статистику по ресурсам ЦП и памяти для каких-то конкретных частей?
Алексей Рыбак: Давайте, я попробую повторить, что она умеет, а вы скажете, что вы имеете в виду, потому что я не до конца вас понял. На весь этап выполнения запроса, с начала и до конца, на скрипт, есть некие соответствующие, назовем их «таймеры по умолчанию». Для целого запроса можно узнать, сколько каких ресурсов и т.д. И дальше есть так называемые «ручные таймеры», между которыми мы можем померить, грубо говоря, все то же самое. Каких таймеров вам не хватает?
Вопрос из зала: Я так понял, что таймер считает именно время только выполнения куска, или все-таки абсолютно все?
Алексей Рыбак: На данный момент действительно, по-моему, он считает только время, но мне кажется, что в скором времени мы туда добавим и rusage, смотря насколько это понадобится. В каком-то виде это должно войти.
Вопрос из зала: Вопрос из Интернета. Как реализовано шардирование (англ. sharding) и балансировка нагрузки между серверами front-end на Badoo?
Алексей Рыбак: Шардинг реализован динамическим образом, через «виртуализацию физических координат». Но я боюсь, что очень кратко на этот вопрос ответить не смогу. Грубо говоря, у нас есть некая виртуальная координата… Мы называем ее «спотом», можно называть «шардом», которая "мапится" на сервер, префикс таблицы и префикс базы данных. И конкретные пользователи привязываются к такому виртуальному хранилищу. Был еще вопрос про раскидывание трафика – этим занимается оборудование.
Вопрос из зала: Спасибо за доклад, я хотел спросить, используете ли вы какие-то PHP-фреймворки в работе? И еще – используете ли вы ORM, или же у вас все на plainSQL-запросах или что-то свое?
Алексей Рыбак: У нас все на plainSQL-запросах, ORM мы не используем, а фреймворк у нас свой, но не очень хороший.
Вопрос из зала: Поэтому вы его в Open Source, наверное, и не предлагаете?
Алексей Рыбак: Да, дело в том, что фреймворк – это последняя вещь, которую вообще стоит пускать в Open Source, потому что я вижу его как хороший фреймворк, действительно хороший фреймворк.
Вопрос из зала: Имеются в виду те, которые во всех случаях хорошие?
Алексей Рыбак: Да.
Вопрос из зала: Потому что, в принципе, каждый фреймворк, он для какой-то ситуации хорош?
Алексей Рыбак: Конечно, конечно. Но, может быть, я плохо смотрел.
Вопрос из зала: Массивы – это циклы, по которым нельзя делать условий?
Алексей Рыбак: Да, для того чтобы сделать условия, необходимо предварительно правильным образом сгенерировать этот массив. Допустим, есть вложенный блок, и в первой операции этот вложенный блок не должен быть показан, а во второй операции он должен быть показан. Допустим, нужно показать внешний блок два раза, а внутренний блок только один раз. Это значит, что внешняя часть структуры данных будет состоять из двух элементов – первый элемент внутри себя не будет содержать соответствующий ключ для блока, а второй элемент будет, и автоматически эта штука будет показана так, как нужно.
Вопрос из зала: То есть фактически она умеет вносить массивы?
Алексей Рыбак: Да.
Вопрос из зала: Вопрос по системе Pinbа. Было упомянуто имя скрипта, "script name", что это за параметр и передается ли "request uri"?
Рыбак: Попрошу разработчика мне помочь.
Разработчик: Судя по всему, довольно популярный вопрос. Есть популярное заблуждение: мы "профайлим" не uri, не url, к которому обращаются, мы "профайлим" скрипт. И получаем информацию по скрипту, который сейчас выполняется, поэтому, естественно, по умолчанию имя скрипта отправляется на сервер. У него стандартный фреймворк и обработчик, все это реализовано через index.php, и в конце концов это "перебрасывается" каким-то образом на другие скрипты. Получается в результате, что "профайлится" index.php, в котором масса включений. Для этих случаев есть функция, которой можно какое-то виртуальное имя поставить, условно говоря, если в индекс PHP вводится анкета, то можно вместо этого анкетой пользоваться. Тогда на сервер будет приходить анкета, как будто это анкета PHP.
Вопрос из зала: А можно ли сделать, чтобы это был просто запрос?
Разработчик: Пожалуйста, подставляй туда запрос - из переменной, не вопрос.
Вопрос из зала: То есть это в рекомендации написано как сделать?
Разработчик: Функция там описана, конечно.
Вопрос из зала: Вопрос по поводу мультиязычности. Каким образом у вас организован парсинг шаблонов? У вас есть какие-то маркеры для разметки фраз внутри шаблонов, или просто используется regexp? Есть опасность в этом случае просто потерять единицу смысла. То есть переводить ее в куски невменяемые, которые переводчик потом не сможет связать между собой, потому что грамматическая структура у этих кусков немного разная. То есть то, что звучит в три слова на русском языке, не звучит в три слова на английском, и тем более на китайском.
Алексей Рыбак: Да, конечно. Можно сказать так. Во-первых, эта структура, она определяется тем, как расположены блоки, поэтому первым делом мы делали так: мы хотели расширять фразы максимально до блоков, и пусть переводят блок. Вот. Мы немножко видоизменили этот алгоритм в результате, но мы действительно используем так называемый метод расширения. Мы заменяем блоки и переменные на некоторые очень короткие и компактные фразы. Переводчик, когда переводит, их видит и может их поменять местами. Проблема: есть какой-то кусок, который приходит к нам в циклах, и где-то слово должно стоять до, где-то должно стоять после. Я вас правильно понял?
Вопрос из зала: Наверное, пример такой ошибки будет у нас: «Пожалуйста, обратитесь в службу поддержки». Слово «поддержки» - это ссылка. В шаблоне. И так получится, что «пожалуйста обратитесь» будет одна лексема, а «поддержка» - другая.
Алексей Рыбак: В данном случае это будет одна лексема. Поскольку это, скорее всего, будет один блок…
Вопрос из зала: У меня тоже вопрос по лексемам. На каждый язык – копия шаблонов.
Алексей Рыбак: Это не копия. Дело в том, что там половина вещей – это HTML, а половина – текст на данном языке, но если вы считаете, что это копия…
Вопрос из зала: У меня вопрос вот с чем связан. Если мы нашли какую-то ошибку, например, в верстке, в одном каком-то исходном шаблоне, который был самым главным, как потом отражается на всех остальных копиях путей этих шаблонов?
Алексей Рыбак: Надо шаблоны повторно генерировать.
Вопрос из зала: То есть они генерируются автоматически?
Алексей Рыбак: Это происходит при любой выкладке.
Вопрос из зала: И все эти изменения, которые внесли переводчики, они же и будут использоваться?
Алексей Рыбак: Конечно.
Вопрос из зала: У меня такой вопрос – получается, у вас на каждый запрос приходится по пять минут. То есть у вас на сервер отсылается куча информации, по всем таймерам. Если у нас, скажем, миллион запросов в день и порядка ста таймеров, у нас получается за день разрастается таблица до…
Алексей Рыбак: Да, я понял, я расскажу сейчас. Во-первых, это режим реального времени, и он держит в "мозгах", сколько скажете. У нас где-то в среднем, я думаю, на одни дата-центр приходит порядка 5-10 тысяч запросов в секунду на PHP, и мы за 5 минут, за 300 секунд, храним в режиме реального времени все "сырые" данные, все отчеты. Потом они "протухают". Если вам нужно рисовать графики – пожалуйста, забирайте оттуда информацию и кладите. Но потом данные "протухают" и очищаются. Вот посчитайте. Возьмем нижнюю границу – 5 тысяч запросов в секунду. С такой скоростью на сервер "валит" HTTP, это мало для этого сервера, и я думаю, что он совершенно спокойно будет обрабатывать и в разы больше, а в "мозгах" он держит, сколько скажете, но в данном случае – 5 минут. 300 умножаете на 5000, сколько получится? Получится достаточно много. Порядка миллиона, может, 10 миллионов, но это не цифры. Это все держится в памяти, никуда не складывается. И через какое-то время, если вам нужно рисовать графики, обращаетесь туда, запрашиваете информацию, складируете ее куда-нибудь еще.
Вопрос из зала: Какую служебную информацию накладывает клиент?
Алексей Рыбак: Вы знаете, мы ее если и замечали, то там не больше процента. Если таймеры включены – не больше процента. (Комментарий: если таймеры выключены – количество служебной информации почти нулевое.)
Вопрос из зала: У меня такой странный вопрос. Я не знаю, как вы, я иногда сталкивался с проблемой… какая-то внутренняя… у нее есть система конфигурации, есть свои фреймворки, и как ты говорил, выпускать фреймворки в свет – последнее дело…
Алексей Рыбак: Да, там обычно просят очень много…
Вопрос из зала: Стояла такая проблема – вы написали хорошую штуку, но она содержит много личного, корпоративного, а на то, чтобы переписать на обычный код, потребуется достаточно большое количество времени…
Алексей Рыбак: Да, я согласен. То есть, вопрос: не было ли проблемой для этих проектов наличие каких-то коммерческих секретов?
Вопрос из зала: Ну, я так полагаю, что не было.
Алексей Рыбак: Ну, в данном случае, ты и сам понимаешь, что не было, потому что в этом случае оно очень хорошо ложилось как инструмент и разрабатывалось как инструмент. То есть это не конкретный инструмент, который делает бизнес-функционал. Это некий инструмент, при помощи которого кто-то сделает функционал, и это очень важно. Соответственно, ничего в бизнес-логике, никаких вещей, связанных с коммерческой тайной. Ну, наверное, какие-то функционалы, которые вошли в FPM - может, они и не нужны вовсе. 
Вопрос из зала: Это на самом деле касается, наверное, больше сервиса Pinba. Там есть, наверное, какие-то удачные места в коде, которые хотелось бы завязать еще где-то? Дальше это объединяется в библиотеку и через некоторое время получается, что есть демон отдельный сам по себе, в нем количество приватного кода…
Алексей Рыбак: А вопрос-то в чем?
Вопрос из зала: Может, у тебя есть какой-то опыт по борьбе с таким…
Алексей Рыбак: Опыт очень простой: нужно в таком случае действительно проектировать не конкретный функционал, а сначала инструмент, потом функционал, и тогда инструмент получится готовый к выходу с открытыми исходниками автоматом, а иначе, наверное, он будет содержать какую-то часть, которую открыть нельзя.
Вопрос из зала: У меня быстрый вопрос. Мне очень понравилось, что Blitz по идеологии напоминает XML/XSLT. Почему его не используете их? Скорость?
Алексей Рыбак: Не только. Скорость, конечно, является проблемой, но вообще XML/XSLT это достаточно сложно. Во-первых, это требует много ресурсов, не только по ЦП, но и по памяти. Во-вторых, все известные мне случаи с более или менее удобным использованием и применением XML/XSLT были связаны с тем, что набирался достаточно интересный вид людей, такие XML/XSLT-разработчики. Мне он не очень понятен. То есть мне кажется, что если ты программист, то ты программист, а если ты занимаешься версткой – ты занимаешься версткой. Наличие какой-то сущности, с одной стороны эти люди должны хорошо знать HTML, с другой стороны должны неплохо программировать,- это мне кажется экономически нецелесообразным. Может быть, я ошибаюсь.
Вопрос из зала: Добрый день. Спасибо за конференцию. У меня вопрос. Я так понимаю, это тестирование производительности. Как вы обрабатываете логические ошибки, которые случаются на всех этих серверах back-end (например, PHP-код понял, что данные не валидны). У вас есть syslog или что-то свое?
Алексей Рыбак: До сих пор, к сожалению, какого-то стопроцентно удобного решения у нас нет, но у нас есть кастомный, свой сборщик log-файлов с данными об ошибках. Если совсем на пальцах, то он работает очень просто: один раз в какое-то время – это время порядка одной минуты, мы просто вручную собираем log-файл в центральное место, выстраиваем статистику, сколько произошло ошибок, за какое время, и часто в нее смотрим. Если выпадает много ошибок, начинаем разбираться, соответственно, можно прорегистрировать какие-то. Но какого-то такого хорошего, удобного сборщика через syslog у нас нет. То есть PHP настроен писать все свои ошибки в log-файл с данными об ошибках, мы учитываем это.
Вопрос из зала: Я говорю не совсем об этом, а о ситуации, когда в скрипт, например, пришел "кривой" id.
Алексей Рыбак: Обычно это приводит к тому, что разработчик что-нибудь кидает в log-файл с данными об ошибках.
Вопрос из зала: А syslog не используется по каким-то причинам или просто руки не дошли?
Алексей Рыбак: Просто руки не дошли. Я думаю, что будем смотреть.
Вопрос из зала: Вопрос о выкладке проектов в Open Source. Было ли желание получить какую-то наработку профита?
Алексей Рыбак: Спасибо, отличный вопрос. Я бы даже его сформулировал несколько шире, если позволите: какие цели вы преследуете, занимаясь всем этим? Значит, помимо желания потешить себя и все такое прочее – тут понятно, авторская мотивация у каждого человека в той или иной степени присутствует… Я могу сказать про Blitz. Дело в том, что мы обкатали этот движок на некотором количестве достаточно известных проектов еще до того, как включили его в продажи. Я ответил на ваш вопрос? На самом деле, если по-хорошему, получил много-много отзывов, много людей стало уводить проект не туда, куда я хотел, но баги находились значительно быстрее. В первую очередь, важно хорошее сообщество, хорошее тестирование, которое помогает сделать качественный продукт и качественный дизайн продукта. Потому что люди начинают пользоваться и делают что-то не так, как ты привык, и они тебе говорят: «Слушай, ты чё такое написал?»
Вопрос из зала: Вопрос достаточно короткий. Для перевода каких-то ресурсов в будущем как много времени потребуется?
Алексей Рыбак: Мы очень быстро поняли, что, скорее всего, если мы будем искать что-то, то это займет, может быть, одну неделю, может быть, две, потому что нужно же как-то обкатать и так далее. Так вот, то решение, о котором я сказал, мы сделали буквально за два дня, первую версию. А поскольку для нас скорость очень важна в данном случае, и нас более-менее все устроило, то мы дальше даже не стали двигаться. Кроме gettext, я ничего не знал про это. У нас вообще потребление ЦП на вебах важно в ситуации, когда динамически вызывается постоянно какая-то подстановка чего-то на что-то, такое решение нас не устроило. Что-то еще искать?.. Мне показалось, что там оно все приблизительно такое…
Вопрос из зала: У меня еще один хороший вопрос по Open Source. Не боитесь ли вы, что изменения в Open Source-версии как-то скажется на стабильности вашего сервиса? То есть, грубо говоря, вам функционала достаточно, вам изменений не нужно, а изменения вносятся, вы выкатываете новую версию, у вас - оп! - и все сломалось в том кейсе, который вы используете…
Алексей Рыбак: Значит… отличный вопрос! Дело в том, что после того, как мы начинаем активно что-то использовать, мы очень аккуратно начинаем ограничивать дальнейшие релизы функционала. И это в некотором смысле начинает убивать проект, потому что пользователи хотят то, хотят се. Сейчас вспомнить, чтобы мы добавили функционал и что-то сломали, я не могу. Обычно это происходит следующим образом. Появляется какой-то человек, который говорит: «Слушай, мне нужна такая-то штука». Мы ему говорим: «Да, когда-нибудь мы это сделаем». Потом мы это сделали, прогнали какие-то тесты и выпустили релиз. Себе мы это не ставим. Где-то мы прокрутили, может быть, на девелоперском сервере, но себе не поставили. Через какое-то время эта штука уезжает к этому клиенту, он где-то себе ее ставит, и мы видим, что шквала плохих отзывов вроде нет, тогда и сами попробуем. 
Вопрос из зала: Сложнее ли разрабатывать такую штуку, соцсеть для зарубежного рынка? Какие там были основные проблемы?
Алексей Рыбак: Если говорить непосредственно о программировании, то мне не очень понятно, в чем вопрос. Особенность того, что это на зарубежный рынок, во-первых, в том, что у нас система географически распределенная, несколько дата-центров, и это было сложно, это было реально сложно. И второй момент – мультиязычность, это было чуть менее сложно. А с точки зрения, думаю, приблизительно одинаково сложно выводить продукт на рынок, что в России, что за рубежом. Есть региональные особенности: в основном за рубежом сервисы знакомств изначально были не похожи на социальную сеть…
Вопрос из зала: Вы вносите изменения в мастер-ветку ... отдаете её клиенту, получаете отзывы… как вы управляете изменениями... вы же не будете держать отдельную ветку для клиента?
Алексей Рыбак: Я так понимаю, что здесь проблема может быть, если при этом одновременно нужно что-нибудь туда внести еще. Да? Дело в том, что мы обычно делаем общедоступным то, что меняют очень редко. Вообще, если посмотреть на наши открытые проекты, то новое вносится туда, дай бог, раз в несколько месяцев. На данном этапе вот так. Ошибки убираем, но мы стараемся в первую очередь обеспечить стабильность… Реально большая проблема была бы, если бы мы все это активно развивали. Так что  однозначного ответа на этот вопрос у меня нет… Господа, есть ли у вас еще вопросы? Если нет, всем спасибо!

Комментарии

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

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

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

Ярослав Ворожко

Ярослав Ворожко

CEO & Founder of IndexDen в IndexDen.

Ярослав Ворожко (Ivinco) делится опытом работы с Real Time индексами.

Андрей Смирнов

Андрей Смирнов

Андрей Смирнов – руководитель разработки, разработчик, фанат Go, Python, DevOps и больших нагрузок. Руководил разработкой backend-сервисов в стартапе Qik, после его покупки продолжил работать в компаниях Skype и Microsoft.

Андрей Смирнов (Qik) рассказывает, для каких задач хорош Twisted, и делится секретами работы с этим фреймворком.