🔎 pprof: Как найти функцию, которая жрет 80% CPU — 28 июня 2026 г. в 07:35:42.619
🔎 pprof: Как найти функцию, которая жрет 80% CPU Сервис на проде внезапно упирается в полку по процессору. Что делает новичок? Сидит и смотрит в исходники взглядом гипнотизера, пытаясь угадать: "Ну, наверное, это регулярка тормозит". Или обкладывает весь код вызовами time.Now() в начале и time.Since() в конце каждой функции. Сеньор открывает терминал, пишет одну команду и через 30 секунд получает точное имя функции, номер строки и процент украденного CPU. Наш инструмент pprof. Он встроен прямо в стандартную библиотеку Go. Работает по принципу семплирования: раз в 10 миллисекунд рантайм Go "замирает", смотрит на стеки всех запущенных горутин и записывает: "Так, в эту микросекунду процессор выполнял функцию json.Unmarshal. Давайте проведем вскрытие за 4 шага. Шаг 1. Включаем «жучка» в коде Всё, что нужно сделать в вашем сервисе - импортировать один пакет со знаком подчеркивания: package main import ( "net/http" _ "net/http/pprof" // <-- Подключаем магию ) func main() { // ... ваша основная бизнес-логика ... // Вешаем pprof на отдельный внутренний порт! go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }() } Нюанс для Senior-ов: Магия _ "net/http/pprof" работает только с дефолтным http.DefaultServeMux. Если вы используете chi, gin или кастомный http.NewServeMux(), роуты профилировщика нужно регистрировать в ваш роутер руками (они лежат в пакете net/http/pprof как обычные хендлеры). Шаг 2. Натравливаем профилировщик Сервис крутится под нагрузкой. Открываем терминал на своей рабочей машине и говорим Go: *"Послушай этот сервер 30 секунд и собери мне статистику"*: go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30 Терминал подумает полминуты и превратится в интерактивную консоль (pprof). Шаг 3. Магия двух колонок Внутри консоли пишем команду top10: Showing nodes accounting for 2.45s, 88.1% of 2.78s total flat flat% sum% cum cum% 1.82s 65.5% 65.5% 1.82s 65.5% regexp.(*bitState).reset 0.34s 12.2% 77.7% 0.45s 16.2% encoding/json.Unmarshal 0.15s 5.4% 83.1% 2.50s 89.9% main.processOrder Главная ловушка новичка - смотреть на колонку cum. • flat - сколько времени процессор провел исключительно внутри тела этой функции. • cum (cumulative) - сколько времени процессор провел в этой функции + во всех функциях, которые она вызвала внутри себя. Пример: У функции main.processOrder показатель cum равен 89.9%, но flat всего 5.4%. Это значит, что сама она почти ничего не считает, она просто вызвала тяжелую регулярку (regexp.reset, у которой flat аж 65.5%). Лечить нужно регулярку, а не processOrder! Шаг 4. Визуальный экстаз (Flame Graph) Смотреть в ASCII-таблицы в 2026 году больно. Выходим из консоли (Ctrl+D) и запускаем ту же команду, но добавив флаг -http: go tool pprof -http=:8080 http://localhost:6060/debug/pprof/profile?seconds=30 В браузере мгновенно откроется шикарный веб-интерфейс. Переключаем вид сверху в режим Flame Graph (Огненный граф): 1. Перед вами "столбы". Чем шире столб по горизонтали - тем больше CPU сожрала эта функция. 2. Кликаем мышкой на самый широкий красный столб. 2. Переключаемся во вкладку Source. 3. Видим наш исходный код, где подсвечена конкретная строчка var re = regexp.MustCompile(...), компилирующая регулярку внутри цикла в хендлере. Прод спасен. 🔥 Senior Warning: Кровавая цена дефолта Никогда не выставляйте порт 6060 наружу в интернет! По адресу /debug/pprof/goroutine?debug=2 любой школьник без авторизации скачает полный текстовый дамп всех запущенных горутин. В их стеках будут лежать ваши пароли от БД в сыром виде, Bearer-токены пользователей и приватные ключи. В продакшене pprof обязан висеть строго на localhost, куда разработчик заходит через kubectl port-forward или SSH-туннель. 👉 @golang_lib

