Как 200 строк на Go
освободили 15 серверов
Pavel Murzakov
Badoo — это
✦ 320 млн пользователей
✦ 400 тыс регистраций каждый день
✦ ~3000 серверов
✦ 70 тыс клиентских запросов на бэкенды в секунду
Grigoriy
Igor
Diego
✦ 4 больших отдела: Features, Billing, Back Office, Platform
✦ В каждом по ~10-25 человек (разделены на команды)
✦ Вопроса о выборе языка не встаёт (есть куча других
важных проблем!)
PHP в Badoo
PHP в Badoo
Здесь может быть
твоё фото
Go в Badoo
Go в Badoo
первый демон на GO в
продакшене - 2014 г.
Go в Badoo
http://goo.gl/cGU1gU
Go в Badoo
PHP в Badoo
Go в Badoo
Здесь может быть
твоё фото
О чём доклад
О чём доклад
✦ Задача, которую мы решали
О чём доклад
✦ Задача, которую мы решали
✦ Прежняя реализация на PHP
О чём доклад
✦ Задача, которую мы решали
✦ Прежняя реализация на PHP
✦ Релаизация на Go
Задача
Профайл
Блок Friends
Блок Friends
Блок Friends
Блок Friends
✦ Основан на друзьях из Facebook
Блок Friends
✦ Основан на друзьях из Facebook
✦ Общие друзья
Блок Friends
✦ Основан на друзьях из Facebook
✦ Общие друзья
✦ Особенно интересны не зарегистрированные на Badoo
Общие друзья
A
X
Y
Мы
Общие друзья
A B
X
Y
Z
Y
Мы Кого мы смотрим
Общие друзья
A B
X
Y
Z
Y
Мы Кого мы смотрим
Общий друг
Реализация #1
✦ Заранее скачиваем с FB друзей текущего пользователя
A X Y
Реализация #1
✦ Заранее скачиваем с FB друзей текущего пользователя
✦ Заранее скачиваем с FB друзей просматриваемого
пользователя
A X Y BZ Y
Реализация #1
✦ Заранее скачиваем с FB друзей текущего пользователя
✦ Заранее скачиваем с FB друзей просматриваемого
пользователя
✦ Пересекаем списки в момент показа
A X Y BZ Y
Реализация #1
✦ Заранее скачиваем с FB друзей текущего пользователя
✦ Заранее скачиваем с FB друзей просматриваемого
пользователя
✦ Пересекаем списки в момент показа
✦ PROFIT!!!!
A X Y BZ Y
Апдейт FB API v2.0
Апдейт FB API v2.0
Было
Friends endpoint
* Зарегистрированные (дали доступ Badoo на FB)
* Незарегистрированные
Апдейт FB API v2.0
Было Стало
Friends endpoint
* Зарегистрированные
* Незарегистрированные
Friends endpoint
* Только зарегистрированные
Апдейт FB API v2.0
Было Стало
Friends endpoint
* Зарегистрированные
* Незарегистрированные
All Mutual Friends endpoint
* Зарегистрированные
* Незарегистрированные
* Нужно 2 fb_id
Friends endpoint
* Только зарегистрированные
Реализация #1 — крах
✦ Предварительно скачать всех друзей мы не можем
✦ => Надо получать общих друзей в онлайне
Реализация #2
✦ Предварительно скачиваем списки зарегистрированных
друзей
Реализация #2
✦ Предварительно скачиваем списки зарегистрированных
друзей
✦ В клиентском запросе на профайл делаем HTTP-запрос в
Facebook за общими друзьями
Время на запросы к FB
Время на запросы к FB
Грубо ~1 сек
Реализация #2
✦ Предварительно скачиваем списки зарегистрированных
друзей
✦ В клиентском запросе на профайл делаем HTTP-запрос в
Facebook за общими друзьями
Мы не можем ждать 1с на отрисовку профиля!
Реализация #3
✦ Предварительно скачиваем списки зарегистрированных
друзей
✦ В клиентском запросе на профайл отдельном асинхронном
запросе делаем HTTP-запрос в Facebook за общими
друзьями
PHP
PHP-FPM
Master process
Child process 1
Child process 2
Child process 3
Child process 4
Child process N
Client 1
Client 2
✦ 100 воркеров PHP-FPM на сервер
Реализация #3
✦ 100 воркеров PHP-FPM на сервер
✦ 100 серверов
Реализация #3
✦ 100 воркеров PHP-FPM на сервер
✦ 100 серверов
✦ => 100 * 100 = 10 000 воркеров
Реализация #3
✦ 100 воркеров PHP-FPM на сервер
✦ 100 серверов
✦ => 100 * 100 = 10 000 воркеров
✦ нагрузка 1 000 rps
Реализация #3
✦ 100 воркеров PHP-FPM на сервер
✦ 100 серверов
✦ => 100 * 100 = 10 000 воркеров
✦ нагрузка 1 000 rps
✦ FB “подтупляет" и начинает отвечать по 10 сек
Реализация #3
✦ 100 воркеров PHP-FPM на сервер
✦ 100 серверов
✦ => 100 * 100 = 10 000 воркеров
✦ нагрузка 1 000 rps
✦ FB “подтупляет" и начинает отвечать по 10 сек
✦ через 10 сек 

1 000 * 10 = 10 000 => все воркеры будут заняты
Реализация #3
100 воркеров на сервер
300 воркеров на сервер
7300 воркеров на сервер
✦ CPU-bound нагрузка (обычно)
✦ Память
✦ Context Switch’es
✦ Непредсказуемость времени ответа от Facebook (нужно
“перезакладываться” в десятки раз)
Воркеры — ограничения
Время на запросы к FB
Непредсказуемо
✦ Предварительно скачиваем списки зарегистрированных
друзей
✦ В клиентском запросе на профайл отдельном запросе
делаем HTTP-запрос в Facebook за общими друзьями
Мы не можем себе позволить долгие запросы!
(вообще)
Реализация #3
PHProxyd
Долгий скрипт
Короткие запросы
Реализация #4
Реализация #4
✦ В коротком клиентском запросе запускаем скрипт в PHProxyd
Реализация #4
✦ В коротком клиентском запросе запускаем скрипт в PHProxyd
✦ Клиент перепроверяет (поллит) результаты
Реализация #4
✦ В коротком клиентском запросе запускаем скрипт в PHProxyd
✦ Клиент перепроверяет (поллит) результаты
✦ В скрипте в PHProxyd делаем HTTP-запрос к FB + небольшая
бизнес логика
Реализация #4
✦ В коротком клиентском запросе запускаем скрипт в PHProxyd
✦ Клиент перепроверяет (поллит) результаты
✦ В скрипте в PHProxyd делаем HTTP-запрос к FB + небольшая
бизнес логика
Теперь точно PROFIT!!!!!!!
Теперь точно PROFIT!!!!!!!
Реализация #4
60M per day => 1000 rps в пике
Реализация #4
Реализация #4
Реализация #4
Реализация #4
Реализация #4
Реализация #4
Реализация #4
Реализация #4
Реализация #4
Реализация #4
Прага Майами
Реализация #4
Прага Майами
Реализация #4
XHProf
PHProxyd — замена
✦ Создание воркера на новый запрос (phproxyd)
PHProxyd — замена
✦ Создание воркера на новый запрос (phproxyd)
Не работает
PHProxyd — замена
✦ Создание воркера на новый запрос (phproxyd)
✦ Преподготовить заранее кучу воркеров (php-fpm)
PHProxyd — замена
✦ Создание воркера на новый запрос (phproxyd)
✦ Преподготовить заранее кучу воркеров (php-fpm)
Не работает
PHProxyd — замена
✦ Создание воркера на новый запрос (phproxyd)
✦ Преподготовить заранее кучу воркеров (php-fpm)
✦ Что-то асинхронное (nodejs, React PHP)
PHProxyd — замена
✦ Создание воркера на новый запрос (phproxyd)
✦ Преподготовить заранее кучу воркеров (php-fpm)
✦ Что-то асинхронное (nodejs, React PHP)
Мы любим PHP…
PHProxyd — замена
✦ Создание воркера на новый запрос (phproxyd)
✦ Преподготовить заранее кучу воркеров (php-fpm)
✦ Что-то асинхронное (nodejs, React PHP)
… но НЕТ!
Мы любим PHP…
PHProxyd — замена
✦ Создание воркера на новый запрос (phproxyd)
✦ Преподготовить заранее кучу воркеров (php-fpm)
✦ Что-то асинхронное (nodejs, React PHP)
✦ Go = пишешь “синхронный” код, работает асинхронно
PHProxyd — замена
✦ Создание воркера на новый запрос (phproxyd)
✦ Преподготовить заранее кучу воркеров (php-fpm)
✦ Что-то асинхронное (nodejs, React PHP)
✦ Go = пишешь “синхронный” код, работает асинхронно
+ “сложились звёзды”
Реализация на Go
✦ Предварительно скачиваем списки зарегистрированных друзей
✦ В коротком клиентском запросе запускаем скрипт в PHProxyd
✦ Клиент перепроверяет (поллит) результаты
✦ В скрипте в PHProxyd делаем HTTP-запрос к FB + небольшая
бизнес логика
Реализация #4 (предыдущая)
✦ Предварительно скачиваем списки зарегистрированных друзей
✦ В коротком клиентском запросе запускаем скрипт в PHProxyd
✦ Клиент перепроверяет (поллит) результаты
✦ В скрипте в PHProxyd делаем HTTP-запрос к FB + небольшая
бизнес логика (походы в MySQL, memcache итд)
Реализация #4 (предыдущая)
Реализация #5 — отличия
✦ Принимает на вход “образ” http/https запроса
Реализация #5 — отличия
✦ Принимает на вход “образ” http/https запроса
✦ Отвечает “in progress”, начинает исполнять запрос асинхронно
Реализация #5 — отличия
✦ Принимает на вход “образ” http/https запроса
✦ Отвечает “in progress”, начинает исполнять запрос асинхронно
✦ На повторные реквесты отвечает “in progress” либо данными
Реализация #5 — отличия
✦ Принимает на вход “образ” http/https запроса
✦ Отвечает “in progress”, начинает исполнять запрос асинхронно
✦ На повторные реквесты отвечает “in progress” либо данными
✦ Всю остальную бизнес-логику (базы, мемкеши) оставляем снаружи
Реализация #5
✦ Глобальный map с заданиями и результатами
Реализация #5
✦ Глобальный map с заданиями и результатами
✦ HTTP-сервер, принимает “json” образ запроса, шлёт его в канал
Реализация #5
✦ Глобальный map с заданиями и результатами
✦ HTTP-сервер, принимает “json” образ запроса, шлёт его в канал
✦ N горутин читают из канала, шлют запрос в FB
Реализация #5
✦ Глобальный map с заданиями и результатами
✦ HTTP-сервер, принимает “json” образ запроса, шлёт его в канал
✦ N горутин читают из канала, шлют запрос в FB
✦ После получения результата пишут его в глобальный map
Реализация #5
✦ Глобальный map с заданиями и результатами
✦ HTTP-сервер, принимает “json” образ запроса, шлёт его в канал
✦ N горутин читают из канала, шлют запрос в FB
✦ После получения результата пишут его в глобальный map
✦ Повторный запрос отдаёт результат из этого map
Реализация #5
✦ Глобальный map с заданиями и результатами
✦ HTTP-сервер, принимает “json” образ запроса, шлёт его в канал
✦ N горутин читают из канала, шлют запрос в FB
✦ После получения результата пишут его в глобальный map
✦ Повторный запрос отдаёт результат из этого map
Дублируем продакшен-трафик!!!
Pprof
Keep-Alive
Прага Майами
+ демон на GO + демон на GO
Вписываем в инфраструктуру
Вписываем в инфраструктуру
✦ JSON over HTTP -> “homemade” proto (GPB/JSON over TCP)
Вписываем в инфраструктуру
✦ JSON over HTTP -> “homemade” proto (GPB/JSON over TCP)
✦ Tests
Вписываем в инфраструктуру
✦ JSON over HTTP -> “homemade” proto (GPB/JSON over TCP)
✦ Tests
✦ Build / Deploy
Вписываем в инфраструктуру
✦ JSON over HTTP -> “homemade” proto (GPB/JSON over TCP)
✦ Tests
✦ Build / Deploy
✦ Statistics / monitoring
Было Стало
Заключение
✦ Badoo, Фича Friends
Заключение
✦ Badoo, Фича Friends
✦ PHP, PHP-FPM, долгие запросы, запросы к внешним сервисам
Заключение
✦ Badoo, Фича Friends
✦ PHP, PHP-FPM, долгие запросы, запросы к внешним сервисам
✦ Go: прототип, pprof, keep-alive
Заключение
✦ Изолируйте проблему
Выводы
Выводы
✦ Изолируйте проблему
✦ Делайте прототипы
Выводы
✦ Изолируйте проблему
✦ Делайте прототипы
✦ Не бойтесь экспериментировать
Спасибо!

«Как 200 строк на Go помогли нам освободить 15 серверов» – Паша Мурзаков (Badoo)