[Перевод] Миллиарды запросов в день встречают Go 1.5
Перевод статьи о том, как компания перевела свою инфраструктуру на Go 1.5 и уменьшила паузы сборщика мусора с ~279 мс до ~10 мс.
Маркетинг системы, ориентированные на клиентов, зависят от сбора и анализа как можно большего количества связанных событий. Клиенты буквально везде, и количество данных растет экспоненциально. Язык 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 часа.