Маркетинг системы, ориентированные на клиентов, зависят от сбора и анализа как можно большего количества связанных событий. Клиенты буквально везде, и количество данных растет экспоненциально. Язык Go играет важную роль в нашей системе сбора данных. Сегодня FLXone обрабатывает 3+ миллиарда запросов в день написанным с нуля нашим приложением.
Наш путь для достижения такой производительности начался с определения ключевых задач стыка маркетинга и рекламы с технологиями:
- должно собираться и обрабатываться огромное количество данных
- клиенты могут генерировать миллионы событий, увеличивая нашу нагрузку за секунды
- отзывчивость (latency) это КЛЮЧ к анализу данных в реальном времени
В 2013 году мы решили, что Go (на тот момент ещё 1.1) выглядит многообещающе, и мы написали первую версию нашего приложения меньше чем за 5 дней, и над ней работало всего 2 программиста. Фишки языка, такие, как горутины и каналы, сильно упростили задачу для написания кода, с обильной конкурентностью (concurrency). Достижение тысяч запросов в секунду на Macbook Pro с минимальными оптимизациями выглядело очень многообещающе.
Приложение, по сути, делает следующее: принимает запросы, с большим количеством URL параметров, в среднем по 1КБ каждый. Сервер парсит запросы, и отправляет сообщение в распределенную очередь. По окончании этого, он возвращает пустой ответ клиенту.
Растем дальше
Как только наш бизнес начал расти, мы увидели, что время отклика стало нарастать. У нас было SLA о 100мс на запрос. И когда мы выросли ещё больше, это становилось проблемой всё больше. Сначала мы решили, что это как-то связано с сетевыми соединениями к серверам, но даже при том, что мы генерировали терабайты данных ежедневно, проблема была в чём-то другом.
Тогда мы принялись анализировать поведение нашей Go программы. В среднем приложение тратило ~2мс на запрос, и это было отлично! У нас оставалось 98мс на сетевой оверхед, SSL рукопожатие, DNS запросы и всё остальное, что держит интернет на плаву.
К сожалению, стандартное отклонение времени отклика была большая, около 100мс. Уложиться в наше требование по SLA стало азартной игрой. С помощью Go пакета «runtime» мы сделали профайлинг нашего приложения и поняли, что нашей проблемой была сборка мусора, которая приводила к тому, что 95-перцентиль времени отклика составлял 279 миллисекунд…
Мы решили переписать большие куски нашего приложения так, чтобы они не генерировали мусора вообще. Это очень уменьшило интервал, на который сборщик мусора останавливал всё приложение, чтобы сделать свои магические действия. Но проблемы со временем отклика оставались всё равно, поэтому мы решили добавить больше нод, чтобы укладываться в наш SLA. При пиковых нагрузках в 80K запросов в секунду, даже минимальный мусор может быть серьёзной проблемой.
И этот день настал
Последние месяцы было много разговоров о Go 1.5. Компилятор полностью был переписан с C на Go, что напоминало мне фильм «Начало» («Inception»). Но более того, был полностью переделан сборщик мусора.
Вчера вечером (19 августа), этот момент наконец-то наступил. Стабильная версия Go 1.5 вышла, с утверждением:
Пауза «остановки мира» сборщика почти всегда будет меньше 10мс, и в большинстве случаев, намного меньше.
Буквально через несколько часов после релиза, мы пересобрали наше приложение с Go 1.5 и запустили наши юнит- и функциональные тесты; всё прошло гладко. Это выглядело слишком хорошо, так что мы проверили функционал ещё и вручную. Через несколько часов мы решили, что будет безопасно выкатить этот билд на одну ноду в продакшене.
Мы дали поработать ей 12 часов и проанализировали новые значения времени отклика: всего запроса, отдельно приложения, и, не менее важный параметр, время пауз сборщика мусора. На графике ниже вы можете увидеть как уменьшился разброс по значениям и среднее значение времени отклика:
Две гистограммы времени отклика приложения (единственно важная для нас вещь). Ось X: время отклика, Ось Y: количество запросов. Слева: сервер бегущий на Go 1.4, справа: сервер бегущий на Go 1.5, разница видна невооруженным взглядом.
Новая версия Go уменьшила наше значение 95-перцентиля сборщика мусора с 279 мс до всего лишь 10 мс. Это фантастическое уменьшение паузы на 96% и это ровно то, что было указано в релиз нотах.
Паузы сборки мусора уменьшились на 96%
Мы решили задеплоить новую версию на остальную часть нашей инфраструктуры (12 дата центров в 7 географических зонах) и увидели, что наше среднее время отклика на запросы уменьшилось на 53%. Это означало, что мы можем без каких либо усилий уложиться в наши 100мс, плюс каждая нода теперь может держать большую нагрузку.
Благодаря отдаче и гибкости нашей команды, релиз Go 1.5 очень сильно улучшил нашу производительность и это произошло за 24 часа.
Комментарии (28)
Pryada
20.08.2015 20:42+2Cледующая заметка в блоге этого автора:
Для HTTPS запросов у нас следующая структура:
[nginx] => [varnish] => [golang-app]
а для HTTP
[varnish] => [golang-app]
Сейчас мы используем чистый net/http для веб сервера, без каких-либо фреймворков с маршрутизацией. Тем не менее, приглядываемся к легковесным реализациям, которые генерируют меньше мусора (или вообще без мусора).
tgz
21.08.2015 08:34Я правильно понял логику:
нам нужна лэтенси ---> берем язык с gc ---> оказывается там фризы ---> о чудо, в версии 1.5 их немного меньше
А если в 1.6 оно снова будет 100мс?
А если бы в 1.5 ничего не изменилось бы?
Я понимаю, это пеар ГОши, но если с другой стороны посмотреть — какая то идиотия, прости господи.
knagaev
21.08.2015 10:16+6Краткое содержание статьи
— Раньше я ездил на велосипеде с подспущенными шинами.
Получалось ездить, но не очень быстро.
Теперь мне накачали шины.
Попробовал объехать вокруг дома — езжу быстрее!!!
Буду теперь ездить быстрее, и все это произошло за пять минут!lexore
21.08.2015 16:12+3Просто автор ещё не отошел от эйфории и не дошел до пункта «сделать выводы».
А выводы могут быть такими «сегодня нас спасли разрабы go, но впредь нужно следить за сборкой мусора».
uzzz
21.08.2015 16:53+2Standard deviation обычно на русский переводится как среднеквадратическое отклонение.
divan0
21.08.2015 16:57Спасибо, это верно. Но звучит слишком «научно» — поправил на «стандартное отклонение», что тоже, вроде как, корректно.
Fesor
21.08.2015 16:59+2Научно не научно, но это общепринятая терминология которой лучше придерживаться.
deniskreshikhin
23.08.2015 12:49+1Да что спорить, в любом случае в статье путаница. Автор называет это standard deviation, но очевидно имеет ввиду просто average request latency.
ingrysty
Раст даже на одном сервере тянет больше 100к запросов в секунду.
Совсем грустно стало за Go после этой статьи.
cy-ernado
На одном ядре.
neolink
Raspberry Pi…
а если серьезно пруфы, код в студию можно будет посмотреть. у меня даже у nginx который на C не получается на ядре делать 100к
cy-ernado
Да это шутки же всё :)
alist
Не, Разбери — это сильно крутая железка.
У меня sim-карта с Java Card Connected на сервлетах 100к запросв в секунду держит. Правда, мы ее деплоим на Nokia 3310
evnuh
Ну да, а у моего брата до сотни за 2 секунды… Вы что и с чем сравниваете, интересно?
sayber
Вы опять тут? =)
gurinderu
А akka хвалится своими 50 млрд сообщений в секунду, а ведь это JVM.
solver
Во первых, не млрд а млн.
Во вторых, это сообщения между акторами внутри JVM, а не запросы по сети.
gurinderu
Да, ошибочка вышла.
Насчет второго хочу сказать, что вам нужно учиться распознавать сарказм)
ingrysty
А в третьих — пони порвал всех в мире акторов.
Link: http://www.ponylang.org/benchmarks_all.pdf