Перевод статьи о том, как компания перевела свою инфраструктуру на 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 часа.

Комментарии (28)


  1. ingrysty
    20.08.2015 17:37
    -6

    Раст даже на одном сервере тянет больше 100к запросов в секунду.
    Совсем грустно стало за Go после этой статьи.


    1. cy-ernado
      20.08.2015 17:46
      +8

      На одном ядре.


      1. neolink
        20.08.2015 17:54
        +8

        Raspberry Pi…
        а если серьезно пруфы, код в студию можно будет посмотреть. у меня даже у nginx который на C не получается на ядре делать 100к


        1. cy-ernado
          20.08.2015 18:08
          +4

          Да это шутки же всё :)


        1. alist
          20.08.2015 18:46
          +26

          Не, Разбери — это сильно крутая железка.

          У меня sim-карта с Java Card Connected на сервлетах 100к запросв в секунду держит. Правда, мы ее деплоим на Nokia 3310

          image


    1. evnuh
      20.08.2015 17:47
      +10

      Ну да, а у моего брата до сотни за 2 секунды… Вы что и с чем сравниваете, интересно?


    1. sayber
      20.08.2015 21:52
      +5

      Вы опять тут? =)


    1. gurinderu
      21.08.2015 10:08
      -3

      А akka хвалится своими 50 млрд сообщений в секунду, а ведь это JVM.


      1. solver
        21.08.2015 10:36
        +3

        Во первых, не млрд а млн.
        Во вторых, это сообщения между акторами внутри JVM, а не запросы по сети.


        1. gurinderu
          21.08.2015 14:35
          +1

          Да, ошибочка вышла.
          Насчет второго хочу сказать, что вам нужно учиться распознавать сарказм)


      1. ingrysty
        21.08.2015 14:03

        А в третьих — пони порвал всех в мире акторов.
        Link: http://www.ponylang.org/benchmarks_all.pdf


  1. Temp1ar
    20.08.2015 20:40
    +15

    Слева: сервер бегущий на Go 1.4, справа: сервер бегущий на Go 1.5, разница видна невооруженным взглядом.

    image


    1. Xazzzi
      20.08.2015 21:24
      +3

      «Сам дурак»
      © Magic Gooddy '98


  1. Pryada
    20.08.2015 20:42
    +2

    Cледующая заметка в блоге этого автора:

    Для HTTPS запросов у нас следующая структура:
    [nginx] => [varnish] => [golang-app]

    а для HTTP
    [varnish] => [golang-app]

    Сейчас мы используем чистый net/http для веб сервера, без каких-либо фреймворков с маршрутизацией. Тем не менее, приглядываемся к легковесным реализациям, которые генерируют меньше мусора (или вообще без мусора).


    1. divan0
      20.08.2015 21:45

      Здорово, спасибо за наводку.


  1. tgz
    21.08.2015 08:34

    Я правильно понял логику:
    нам нужна лэтенси ---> берем язык с gc ---> оказывается там фризы ---> о чудо, в версии 1.5 их немного меньше
    А если в 1.6 оно снова будет 100мс?
    А если бы в 1.5 ничего не изменилось бы?
    Я понимаю, это пеар ГОши, но если с другой стороны посмотреть — какая то идиотия, прости господи.


  1. knagaev
    21.08.2015 10:16
    +6

    Краткое содержание статьи
    — Раньше я ездил на велосипеде с подспущенными шинами.
    Получалось ездить, но не очень быстро.
    Теперь мне накачали шины.
    Попробовал объехать вокруг дома — езжу быстрее!!!
    Буду теперь ездить быстрее, и все это произошло за пять минут!


    1. lexore
      21.08.2015 16:12
      +3

      Просто автор ещё не отошел от эйфории и не дошел до пункта «сделать выводы».
      А выводы могут быть такими «сегодня нас спасли разрабы go, но впредь нужно следить за сборкой мусора».


  1. uzzz
    21.08.2015 16:53
    +2

    Standard deviation обычно на русский переводится как среднеквадратическое отклонение.


    1. divan0
      21.08.2015 16:57

      Спасибо, это верно. Но звучит слишком «научно» — поправил на «стандартное отклонение», что тоже, вроде как, корректно.


      1. Fesor
        21.08.2015 16:59
        +2

        Научно не научно, но это общепринятая терминология которой лучше придерживаться.


        1. deniskreshikhin
          23.08.2015 12:49
          +1

          Да что спорить, в любом случае в статье путаница. Автор называет это standard deviation, но очевидно имеет ввиду просто average request latency.


  1. erlyvideo
    23.08.2015 21:42
    -4

    На что люди не идут, лишь бы сразу на эрланге всё нормально не сделать


  1. wickedweasel
    25.08.2015 12:46
    -2

    Нет ли в статье скрытой рекламы таблеток персен?


  1. guai
    25.08.2015 18:57
    +1

    Что еще за «персентиль»?


    1. divan0
      25.08.2015 19:07

      1. guai
        25.08.2015 20:57

        Я-то догадался, что речь про процентиль, она же перцентиль.


        1. divan0
          25.08.2015 21:44

          Ну я и так, и так встречал. Хотя окей, поправлю.