Решите, что время сборщика мусора службы Go слишком велико

Перепечатано:  GC-пауза службы Go в контейнере часто превышает 100 мс.

Пауза GC службы Go в контейнере часто превышает 100 мс.

Оригинал yifhao  yifhao  2019-11-08 22:03

Паузы GC часто превышают 100 мс

Феномен

Некоторые коллеги сообщили, что недавно начали опробовать k8s компании, и процесс go, развернутый в docker, имеет проблемы, интерфейс долго работает, есть таймауты, логика очень проста, достаточно вызвать хранилище kv, а общее время отклика хранилища kv составляет <5 мс, а объем очень мал, менее 40 запросов в секунду, контейнеру выделяется квота в 0,5 ядра, а ежедневно работающий ЦП имеет менее 0,1 ядра. 

повторяющийся

Я нашел контейнер и отключил трафик доступа. Используйте ab 50 для создания нескольких запросов одновременно. Задержка в сети составляет 60 мс, но среднее время обработки занимает более 200 мс, а 99% занимает 679 мс.

При обработке с аб смотрел инфу по ЦП и памяти,проблем нет.Докер выделяет 0.5 ядра.Здесь не так много используется.

Глядя на мониторинг, GC STW (stop the world) превышает 10 мс, есть много 50-100 мс, и многие из них превышают 100 мс.Гу не утверждал, что паузы GC в основном меньше 1 мс после 1.8?

gc информация и трассировка

Глядя на информацию о времени выполнения процесса, обнаруживается, что памяти очень мало, gc-пауза очень велика, а GOMAXPROCS составляет 76, что соответствует количеству ядер машины. 

export GODEBUG=gctrace=1, перезапустите процесс, чтобы увидеть.Видно, что пауза gc действительно серьезная.

 
 
  1. gc 111 @97.209s 1%: 82+7.6+0.036 ms clock, 6297+0.66/6.0/0+2.7 ms cpu, 9->12->6 MB, 11 MB goal, 76 P

  2. gc 112 @97.798s 1%: 0.040+93+0.14 ms clock, 3.0+0.55/7.1/0+10 ms cpu, 10->11->5 MB, 12 MB goal, 76 P

  3. gc 113 @99.294s 1%: 0.041+298+100 ms clock, 3.1+0.34/181/0+7605 ms cpu, 10->13->6 MB, 11 MB goal, 76 P

  4. gc 114 @100.892s 1%: 99+200+99 ms clock, 7597+0/5.6/0+7553 ms cpu, 11->13->6 MB, 13 MB goal, 76 P

Запустите трассировку службы на сервере с помощью go sdk, а затем загрузите файл трассировки локально, чтобы посмотреть

 
 
  1. curl -o trace.out 'http://ip:port/debug/pprof/trace?seconds=20'

  2. sz ./trace.out

На рисунке ниже показано, что время стенки GC составляет 172 мс, а две фазы STW этого gc, окончание развертки и завершение метки, занимают более 80 мс, почти занимая все время GC, что, конечно, очень ненаучно. . 

Причина и решение

причина

Эта служба работает в контейнере, а контейнер и хост-компьютер совместно используют одно ядро. Количество ядер ЦП, видимых процессом в контейнере, также является количеством ядер ЦП хост-компьютера. Для приложений Go количество P (для GOMAXPROCS) будет установлено по умолчанию как Количество ядер ЦП. Мы также можем видеть из предыдущего рисунка, что GOMAXPROCS равно 76, и каждый используемый P имеет связанный с ним m, поэтому количество потоков также довольно большое. lot, а цифра выше — 171. Однако число, отведенное под CPU quota контейнера, на самом деле не так много, всего 0,5 ядра, а количество потоков довольно много.

Угадайте: для linux cfs (полностью честный планировщик) все потоки (облегченные процессы) в текущем контейнере находятся в группе планирования.Чтобы обеспечить эффективность, для каждой запущенной задачи, если она не заблокирована и т. д. Если причина переключена активно, то по крайней мере гарантируется время работы /proc/sys/kernel/schedminggranularity_ns, которое можно увидеть как 4 мс.

Процесс Go в контейнере неправильно устанавливает количество GOMAXPROCS, в результате получается слишком много исполняемых потоков, и может возникнуть проблема задержки планирования.Бывает, что после того, как поток, который входит в gc для инициации STW, останавливает другие потоки, это отключен планировщиком в течение длительного времени Поток не запланирован, что, по сути, приводит к тому, что время STW становится очень длинным (процесс обработки 0,1 мс при нормальных обстоятельствах становится уровнем 100 мс из-за задержки планирования).

Решение

Решение, поскольку существует слишком много P, которые могут быть запущены, займет виртуальное время работы потока, инициировавшего stw, а квота ЦП невелика.Тогда нам нужно сопоставить P с Квота процессора Мы можем выбрать:

  1. Увеличьте квоту ЦП для контейнера.

  2. Уровень контейнера позволяет процессам в контейнере видеть количество ядер ЦП как квоту.

  3. Установите правильный GOMAXPROCS в соответствии с квотой

Первый способ: Особого эффекта не дает, меняем квоту с 0,5 на 1, принципиальной разницы нет (попробовал, проблема осталась). 

Способ 2: Поскольку я не очень хорошо знаком с k8s, я добавлю его после того, как проведу исследование. 

Третий способ: Самый простой способ настроить GOMAXPROCS — запустить скрипт для добавления переменных окружения 

GOMAXPROCS=2 ./svr_bin Это эффективно, но есть и недостатки: если разворачивается контейнер с большей квотой, скрипт нельзя изменить соответствующим образом.

библиотека uber automaxprocs

У Убера есть библиотека go.uber.org/automaxprocs, при запуске процесса go в контейнере GOMAXPROCS будет установлен корректно Шаблон кода изменен, ссылка на эту библиотеку есть в go.mod

 
 
  1. go.uber.org/automaxprocs v1.2.0

И импортировать в main.go

 
 
  1. import (

  2. _ "go.uber.org/automaxprocs"

  3. )

Эффект

Советы по библиотеке automaxprocs

При использовании библиотеки automaxprocs будут следующие логи:

  1. Для виртуальных машин или физических машин

    В случае 8 ядер: 2019/11/07 17:29:47 maxprocs: оставить GOMAXPROCS=8: квота процессора не определена

  2. Для контейнеров с более чем 1 основным набором квот

    08.11.2019 19:30:50 maxprocs: Обновление GOMAXPROCS=8: определяется из квоты ЦП

  3. Для контейнеров с квотой менее 1 ядра

    08.11.2019 19:19:30 maxprocs: Обновление GOMAXPROCS=1: использование минимально допустимого GOMAXPROCS

  4. Если в докере не задана квота

    07.11.2019, 19:38:34 maxprocs: выход из GOMAXPROCS=79: квота процессора не определена

    На этом этапе рекомендуется явно установить GOMAXPROCS в сценарии запуска.

время ответа запроса

После настройки используйте ab запрос для проверки еще раз.Сетевое время приема-передачи составляет 60 мс, и 99% запросов меньше 200 мс, что раньше было 600 мс.При том же потреблении процессора qps почти удвоился. 

информация о времени выполнения и трассировке сборщика мусора

Поскольку выделено 0,5 ядра, GOMAXPROC распознает его как 1. gc-пауза также очень низкая, в виде десятков нас.В то же время видно, что количество потоков упало с более чем 170 до 11. 

 
 
  1. gc 97 @54.102s 1%: 0.017+3.3+0.003 ms clock, 0.017+0.51/0.80/0.75+0.003 ms cpu, 9->9->4 MB, 10 MB goal, 1 P

  2. gc 98 @54.294s 1%: 0.020+5.9+0.003 ms clock, 0.020+0.51/1.6/0+0.003 ms cpu, 8->9->4 MB, 9 MB goal, 1 P

  3. gc 99 @54.406s 1%: 0.011+4.4+0.003 ms clock, 0.011+0.62/1.2/0.17+0.003 ms cpu, 9->9->4 MB, 10 MB goal, 1 P

  4. gc 100 @54.597s 1%: 0.009+5.6+0.002 ms clock, 0.009+0.69/1.4/0+0.002 ms cpu, 9->9->5 MB, 10 MB goal, 1 P

  5. gc 101 @54.715s 1%: 0.026+2.7+0.004 ms clock, 0.026+0.42/0.35/1.4+0.004 ms cpu, 9->9->4 MB, 10 MB goal, 1 P

переключатель контекста

Ниже приведено сравнение результатов perf stat с параллелизмом 50 и общим числом обработанных запросов 8000. Число ядер ЦП по умолчанию — 76 P, переключение контекста происходит более 130 000 раз, а pidstat проверяет, что системный ЦП потребляет 9% ресурсов процессора. ядро После установки количества P в соответствии с номером квоты переключение контекста происходит только более 20 000 раз, а процессор потребляет 3% ядер. 

Анализ принципа automaxprocs

Как эта библиотека устанавливает GOMAXPROCS в соответствии с квотой?Код немного запутанный.Прочитав его,принцип не сложен.Докер использует cgroup для ограничения загрузки ЦП контейнера,а квоту ЦП можно получить с помощью cpu.cfsquotaus/cpu.cfsperiodus настроен контейнером, Итак, ключ в том, чтобы найти эти два значения для контейнера.

Получить информацию о монтировании cgroup

кошка /proc/self/mountinfo

 
 
  1. ....

  2. 1070 1060 0:17 / /sys/fs/cgroup ro,nosuid,nodev,noexec - tmpfs tmpfs ro,mode=755

  3. 1074 1070 0:21 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory

  4. 1075 1070 0:22 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,devices

  5. 1076 1070 0:23 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,blkio

  6. 1077 1070 0:24 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,hugetlb

  7. 1078 1070 0:25 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuacct,cpu

  8. 1079 1070 0:26 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuset

  9. 1081 1070 0:27 / /sys/fs/cgroup/net_cls rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,net_cls

  10. ....

cpuacct, cpu находится в каталоге /sys/fs/cgroup/cpu, cpuacct.

Получить подкаталог cgroup контейнера

кошка /proc/self/cgroup

 
 
  1. 10:net_cls:/kubepods/burstable/pod62f81b5d-xxxx/xxxx92521d65bff8

  2. 9:cpuset:/kubepods/burstable/pod62f81b5d-xxxx/xxxx92521d65bff8

  3. 8:cpuacct,cpu:/kubepods/burstable/pod62f81b5d-xxxx/xxxx92521d65bff8

  4. 7:hugetlb:/kubepods/burstable/pod62f81b5d-5ce0-xxxx/xxxx92521d65bff8

  5. 6:blkio:/kubepods/burstable/pod62f81b5d-5ce0-xxxx/xxxx92521d65bff8

  6. 5:devices:/kubepods/burstable/pod62f81b5d-5ce0-xxxx/xxxx92521d65bff8

  7. 4:memory:/kubepods/burstable/pod62f81b5d-5ce0-xxxx/xxxx92521d65bff8

  8. ....

cpuacct и cpu контейнера находятся в подкаталоге /kubepods/burstable/pod62f81b5d-xxxx/xxxx92521d65bff8.

рассчитать квоту

 
 
  1. cat /sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/pod62f81b5d-5ce0-xxxx/xxxx92521d65bff8/cpu.cfs_quota_us

  2. 50000

  3. cat /sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/pod62f81b5d-5ce0-xxxx/xxxx92521d65bff8/cpu.cfs_period_us

  4. 100000

Эти два делятся, чтобы получить 0,5, если оно меньше 1, GOMAXPROCS устанавливается равным 1, а если больше 1, ему присваивается рассчитанное число.

основная функция

Основные функции в библиотеке automaxprocs следующие, где cg — все пути конфигурации анализируемой контрольной группы.Прочитайте cpu.cfs_quota_us и cpu.cfs_period_us соответственно, а затем вычислите. 

официальный выпуск

Я гуглил, и кто-то задавал этот вопрос 

время выполнения: длительные паузы GC STW (≥80 мс) #19378 https://github.com/golang/go/issues/19378

Подведем итог

  1. Количество ядер, видимых процессом в контейнере, — это количество ядер ЦП родительской машины.Как правило, это значение больше 32, что приводит к тому, что процесс go устанавливает для P большее число и открывает множество P и потоков.

  2. Как правило, квота контейнера не велика, 0,5-4. Планировщик linux принимает контейнер как группу, и планирование потоков внутри справедливо, и каждый исполняемый поток будет гарантировать определенное время работы, потому что их много. потоки и квота мала, хотя количество запросов небольшое, но есть много переключений контекста, это также может вызвать задержку в планировании потока, который инициирует stw, в результате чего время stw возрастет до уровня 100 мс, что сильно влияет на запрос

  3. Используя библиотеку automaxprocs, количество GOMAXPROCS и P может быть правильно установлено в соответствии с квотой процессора, выделенной для контейнера, а количество потоков может быть уменьшено, чтобы пауза GC была стабильной на уровне <1 мс. потребление, количество запросов в секунду может быть удвоено, а среднее время отклика уменьшено с 200 мс до 100 мс, переключение контекста потока уменьшено до 1/6 от исходного

  4. При этом также кратко анализируется принцип работы библиотеки: находим каталог cgroup контейнера, вычисляем cpuacct, cpu.cfs_quota_us/cpu.cfs_period_us под cpu, что является количеством выделенных процессорных ядер.

  5. Конечно, если процесс в контейнере увидит, что количество ядер процессора соответствует выделенной квоте, то и эту проблему можно решить, я мало что знаю об этом аспекте.

Статья была изменена 2019-11-08

Уведомление:

Квота ЦП контейнера должна быть настроена, а не по умолчанию

​​​​​

 

Supongo que te gusta

Origin blog.csdn.net/Horsdy123/article/details/124402141
Recomendado
Clasificación