pprof снимает профили CPU, кучи, блокировок и горутин. Разговор про «медленно» без цифр и без профиля обычно ничем не кончается.
Рантайм и инструменты Go golang собеседование
pprof — набор сэмплов: где тратится CPU, какие объекты держат кучу, где застряли горутины, кто держит mutex. Это не логи и не trace распределённых спанов, хотя их сочетают.
Наблюдаемость начинается с метрик: RPS, P99, CPU, RSS. Поиск «почему» часто заканчивается профилем. Снимать лучше с инстанса под боевой нагрузкой, не с ноутбука в простое. Открытый pprof в интернет — утечка стеков и возможность DoS съёмкой CPU. В проде — внутренняя сеть, авторизация, или съём по требованию.
Аналогия: разные анализы у врача. Температура (метрика) сказала «плохо». Дальше снимок костей не заменяет анализ крови. CPU-профиль молчит, пока сервис ждёт Postgres: процессор спит, болен не он.
Какие профили
CPU profile — сэмплы стека по таймеру: где процессор. Не показывает ожидание сети. Heap — аллокации и inuse: кто держит байты сейчас (inuse_space) против кто много выделял за жизнь (alloc_space). Для «растёт RSS» смотрят inuse. Goroutine — стеки всех G: сколько висит в receive, select, syscall. Mutex/block — споры за замки.
import _ "net/http/pprof"
func main() {
go func() {
_ = http.ListenAndServe("localhost:6060", nil)
}()
// go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
// go tool pprof http://localhost:6060/debug/pprof/heap
// go tool pprof http://localhost:6060/debug/pprof/goroutine
}
Подключить pprof к служебному HTTP и снять CPU и heap.
Пример. P99 вырос, CPU в покое, RSS стабилен. CPU-профиль почти пустой. Смотрят goroutine и block: тысячи G в chan receive или в Lock. Или traces в БД: ждут запрос, а не крутят JSON.
Как читать
В pprof полезны top, list, flame graph. Cum — с детьми, flat — сама функция. Оптимизировать библиотечную runtime.mallocgc бессмысленно: смотрят, кто её вызывает из вашего кода. Для CPU «медленно потому что JSON» видно. «Медленно потому что Postgres» — нет, пока не смотрите traces и explain.
Goroutine profile с десятками тысяч G в одном receive — утечка или слишком широкий fan-out. Heap с жирной строкой в пакете кэша — забыли TTL. Сравнение двух heap (до и после) ловит рост. Не путайте alloc_objects (шум мелких аллокаций) с inuse_space (кто держит память сейчас).
Цепочка расследования
- Метрика: P99 вырос, CPU или RSS.
- Гипотеза: CPU в хендлере / аллокации / горутины / замок.
- Профиль, который эту гипотезу проверяет.
- Фикс и снова метрика — без этого «покрутили pprof» ничего не доказывает.
Типичная ошибка
Держать pprof на :80 без защиты. Смотреть только CPU, когда сервис ждёт БД. Судить о куче по alloc_objects без inuse_space. Снимать профиль с простоя и удивляться, что «всё в runtime».
На собеседовании
Цепочка: метрика (P99) → нужный профиль → гипотеза → фикс → снова метрика. Heap для RSS — inuse. Зависшие горутины — goroutine и block. Логи без профиля на вопросе про скорость — слабый ответ.