Как встроить стандартный профайлер в свое приложение?go-98

Go имеет мощный встроенный профайлер (pprof), который позволяет анализировать различные аспекты работы программы. Вот как его правильно интегрировать в приложение.

1. Подключение пакета pprof

Стандартный пакет net/http/pprof автоматически регистрирует обработчики для профилирования на существующем HTTP-роутере.

import _ "net/http/pprof"

Этот импорт регистрирует следующие эндпоинты:

  • /debug/pprof/ - общая информация
  • /debug/pprof/heap - профиль памяти
  • /debug/pprof/profile - CPU профиль
  • /debug/pprof/goroutine - стек горутин
  • /debug/pprof/block - блокировки
  • /debug/pprof/mutex - мьютексы
  • /debug/pprof/trace - трассировка выполнения

2. Базовый пример интеграции

package main

import (
    "log"
    "net/http"
    _ "net/http/pprof" // автоматически добавляет обработчики pprof
    "time"
)

func busyWork() {
    for {
        time.Sleep(1 * time.Millisecond)
    }
}

func main() {
    // Запускаем горутину с нагрузкой
    go busyWork()

    // Запускаем HTTP сервер для профилирования
    log.Println(http.ListenAndServe("localhost:6060", nil))
}

После запуска откройте в браузере http://localhost:6060/debug/pprof/

3. Ручное профилирование в коде

Для точечного профилирования можно использовать API pprof напрямую:

CPU профилирование

import (
    "os"
    "runtime/pprof"
)

func startCPUProfile() {
    f, err := os.Create("cpu.prof")
    if err != nil {
        log.Fatal(err)
    }
    if err := pprof.StartCPUProfile(f); err != nil {
        log.Fatal(err)
    }
    // Вызовите pprof.StopCPUProfile() перед выходом
}

Профилирование памяти

func writeHeapProfile() {
    f, err := os.Create("heap.prof")
    if err != nil {
        log.Fatal(err)
    }
    if err := pprof.WriteHeapProfile(f); err != nil {
        log.Fatal(err)
    }
    f.Close()
}

4. Анализ профилей

Собранные профили можно анализировать с помощью go tool pprof:

# CPU профиль
go tool pprof cpu.prof

# Память (из HTTP)
go tool pprof http://localhost:6060/debug/pprof/heap

# Интерактивный CPU профиль 30 секунд
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30

В интерактивном режиме доступны команды:

  • top - топ функций по использованию ресурсов
  • list FunctionName - детализация по конкретной функции
  • web - визуализация графа вызовов (требуется Graphviz)

5. Продвинутые сценарии

Профилирование горутин

import "runtime/pprof"

func saveGoroutineProfile() {
    f, err := os.Create("goroutines.pprof")
    if err != nil {
        log.Fatal(err)
    }
    if err := pprof.Lookup("goroutine").WriteTo(f, 0); err != nil {
        log.Fatal(err)
    }
    f.Close()
}

Профилирование блокировок

import "runtime"

func enableBlockProfile() {
    runtime.SetBlockProfileRate(1) // Записывать каждую блокировку
}

Трассировка выполнения

import (
    "os"
    "runtime/trace"
)

func startTrace() {
    f, err := os.Create("trace.out")
    if err != nil {
        log.Fatal(err)
    }
    if err := trace.Start(f); err != nil {
        log.Fatal(err)
    }
    // Вызовите trace.Stop() перед выходом
}

Анализ трассировки:

go tool trace trace.out

6. Интеграция с фреймворками

Для популярных веб-фреймворков нужно явно подключить роуты:

Gin

import (
    "github.com/gin-contrib/pprof"
    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()
    pprof.Register(router) // добавляет /debug/pprof/* роуты
    router.Run(":8080")
}

Echo

import (
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
    "net/http/pprof"
)

func main() {
    e := echo.New()

    // Добавляем pprof роуты
    pprofGroup := e.Group("/debug/pprof")
    pprofGroup.GET("", echo.WrapHandler(http.HandlerFunc(pprof.Index)))
    pprofGroup.GET("/:profile", echo.WrapHandler(http.HandlerFunc(pprof.Index)))
    // ... остальные обработчики

    e.Start(":8080")
}

7. Профилирование в production

  1. Безопасность: Ограничьте доступ к pprof эндпоинтам
  2. Ресурсы: CPU профилирование создает нагрузку
  3. Длительность: Кратковременные профили (5-30 секунд)
  4. Пример защищенного доступа:
func pprofAuth(h http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if !isAuthorized(r) {
            http.Error(w, "Forbidden", http.StatusForbidden)
            return
        }
        h(w, r)
    }
}

// Защищенные роуты
http.HandleFunc("/debug/pprof/", pprofAuth(pprof.Index))

Резюмируем

Встроенный профайлер Go предоставляет мощные инструменты для анализа производительности. Для базового использования достаточно импортировать net/http/pprof и запустить HTTP-сервер. Для продвинутых сценариев используйте прямое API pprof и runtime-пакеты. Всегда соблюдайте меры предосторожности при профилировании в production.