профилирование обработчика http в go lang

Я пытаюсь профилировать свой обработчик http, написанный на go. Который на каждый http-запрос загружает изображение с S3, изменяет его размер/обрезает и пишет в ответ.

Я следил за этим link и попытался профилировать мой код, как указано, используя как простой, так и сложный метод. Теперь, когда я использую следующую строку, как указано в коде.

defer profile.Start(profile.CPUProfile).Stop() 

В файле /tmp/profie[some number]/cpu.pprof ничего не пишет

func main() {                                    

       defer profile.Start(profile.CPUProfile).Stop()   

      if err := http.ListenAndServe(":8081", http.HandlerFunc(serveHTTP)); err != nil {
       logFatal("Error when starting or running http server: %v", err)
    }       
}

func serveHTTP(w http.ResponseWriter, r *http.Request) {
        keyName := r.URL.Path[1:]
        s3Client := s3.New(session.New(), &aws.Config{Region: aws.String(region)})
        params := &s3.GetObjectInput{
        Bucket: aws.String(bucketName),
        Key: aws.String(keyName),
        }

    mw := imagick.NewMagickWand()
    defer mw.Destroy()
    ...
}

Более того, когда я использовал строку defer profile.Start(profile.CPUProfile).Stop() внутри serveHTTP, например:

func serveHTTP(w http.ResponseWriter, r *http.Request) {

    defer profile.Start(profile.CPUProfile).Stop()
    ......
}

Он создает несколько файлов в папке /tmp/profile[some number]. Итак, первый вопрос, почему он не записывается в файл, а во-вторых, не должен ли он находиться внутри serveHTTP method, потому что сервер запустится только один раз. Следовательно, main() будет вызываться один раз, тогда как serveHTTP будет вызываться при каждом запросе.

Часть 1

 .               124:   s3Client := s3.New(session.New(), &aws.Config{Region: aws.String(region)})
 .          .    125:        params := &s3.GetObjectInput{
 .          .    126:       Bucket: aws.String(masterBucketName),
 .          .    127:       Key: aws.String(keyName),
 .    32.01kB    128:       }
 .          .    129:
 .          .    130:   mw := imagick.NewMagickWand()
 .          .    131:   defer mw.Destroy()
 .          .    132:   
 .          .    133:   out, err := s3Client.GetObject(params)          
 .          .    134:
 .          .    135:   if strings.EqualFold(keyName[strings.LastIndex(keyName,".")+1:len(keyName)], "gif") {
 .          .    136:
 .    40.11kB    137:       blobGiff, err := ioutil.ReadAll(out.Body)
 .          .    138:       w.Header().Set("Content-Type", "image/gif")
 .          .    139:       w.Header().Set("Cache-Control", "max-age: 604800, public")  
 .          .    140:       w.Header().Set("Last-Modified", time.Now().Format(http.TimeFormat))
 .          .    141:       w.Header().Set("Expires", time.Now().AddDate(1, 0, 0).Format(http.TimeFormat))  
 .          .    142:

Часть 2 :

                                   else {
         .          .    167:       img, err := ioutil.ReadAll(out.Body)
         .          .    168:       if err != nil {
         .          .    169:          
         .          .    170:          w.WriteHeader(http.StatusNotFound) 
         .     1.56MB    171:          return      
         .          .    172:       }   

Кроме того, в приведенных выше двух частях строки 128, 137 и 171 есть утечки памяти, верно? Кроме того, я не нахожу возможности закрыть/уничтожить s3Client и blobGiff (байт []).


person Naresh    schedule 12.02.2016    source источник
comment
defer в main никогда не запускается, потому что http.ListenAndServe блокируется навсегда. Вы получаете несколько файлов, когда помещаете их в обработчик запросов, потому что обработчик вызывается несколько раз и каждый раз создает новый профиль.   -  person thwd    schedule 12.02.2016
comment
но если я напишу это перед вызовом serveHTTP. В файл ничего не пишет.   -  person Naresh    schedule 12.02.2016
comment
Вы не можете использовать строку defer profile.Start(profile.CPUProfile).Stop() в main, так как отсрочка никогда не будет вызвана (ListenAndServe блокируется и ждет вечно). Когда вы вызываете defer profile.Start(profile.CPUProfile).Stop() в своей функции serveHTTP, профилировщик уже запущен (из основного), поэтому он ничего не запишет (поскольку профилировщик не может запускаться более одного раза за раз).   -  person nussjustin    schedule 12.02.2016
comment
Вы не можете профилировать отдельный обработчик. pprof — это профилировщик выборки, поэтому, если обработчик не будет работать очень долго, вы не получите никаких выборок. Профилируйте все свое приложение и просто создайте достаточную нагрузку на обработчик. Это еще проще, если вы импортируете https://golang.org/pkg/net/http/pprof/ и профилируете работающий двоичный файл. .   -  person JimB    schedule 12.02.2016


Ответы (2)


Для профилирования http-сервера во время его работы можно использовать пакет net/http/pprof.

Просто добавь

import _ "net/http/pprof"

к вашему импорту и откройте http://localhost:8081/debug/pprof/ в браузере.

person fl0cke    schedule 12.02.2016
comment
+1. И если вы действительно хотите использовать стандартный профилировщик, используйте os/signal для реализации корректного завершения работы (или библиотеки корректного завершения работы), чтобы ваш отложенный вызов вызывался при необходимости. - person Elwinar; 12.02.2016
comment
@fl0cke Я попробую это. Не могли бы вы также рассказать. Строки, которые я приложил, определенно имеют утечки памяти, и это ясно указывает на то, что это связано с [] byte и s3client. Итак, есть ли способ уничтожить или закрыть их в go lang? - person Naresh; 12.02.2016
comment
@Naresh: такое профилирование не для этого, и эти строки сами по себе не означают утечки памяти. Go собирает мусор, поэтому нет прямого способа уничтожить []byte, а *S3 не имеет метода close. Мы не можем сказать по фрагментам кода, но похоже, что вы не проверяете ошибку, возвращенную от GetObject (всегда проверять ошибки), и не закрываете тело ответа от GetObject. - person JimB; 13.02.2016
comment
@JimB Спасибо. Я обработал ошибку и закрыл тело ответа. Не могли бы вы сказать мне, если я не обработаю ошибку, оставит ли она утечку памяти? Более того, если профилировщик памяти не дает истинных точек утечки памяти, то как мы узнаем, откуда происходит утечка памяти. - person Naresh; 14.02.2016
comment
@fl0cke Как вы и предложили, я импортировал "net/http/pprof" и попытался просмотреть тот же URL. Но это дает ERROR: Not sufficient parameters were provided. Причина, по которой ключ передается клиенту s3, будет пустым. К вашему сведению, как вы можете видеть, я использую мультиплексор по умолчанию в своем коде. - person Naresh; 15.02.2016
comment
@Naresh: в go есть полностью точный сборщик мусора, поэтому, по сути, нет такой вещи, как утечка памяти - единственный способ утечки ресурсов - это запустить код, ссылающийся на них. Профилировщик памяти показывает, где выделена память, но не может сказать, где вы должны были освободить эти ресурсы. Даже не глядя на источник, можно предположить, что ответ ссылается на сетевое соединение и, возможно, на все необходимое для http-запроса, который необходимо очистить. Отсутствие обработки ошибки оставляет возможность получить недопустимый ответ и возможность паники. - person JimB; 15.02.2016

Прежде всего, используйте import "net/http/pprof" НЕ импортируйте _ net/http/pprof. позже никто не узнал pprof в приведенных ниже маршрутах.

Я использовал serverMux/мультиплексор по умолчанию. Но затем я создал свой собственный, поскольку люди предположили, что это влияет на производительность.

myMux := http.NewServeMux()

Затем добавил маршрут для запроса

myMux.HandleFunc("/", serveHTTP)

Кроме того, я также добавил маршруты, чтобы заставить http://localhost:8081/debug/pprof/ работать

        myMux.HandleFunc("/debug/pprof/", pprof.Index)
        myMux.HandleFunc("/debug/pprof/{action}", pprof.Index)
        myMux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)

Итак, окончательный код будет:

импортировать сеть/http/pprof

func main() {                                    
        
        myMux := http.NewServeMux()
        myMux.HandleFunc("/", serveHTTP)

        myMux.HandleFunc("/debug/pprof/", pprof.Index)
        myMux.HandleFunc("/debug/pprof/{action}", pprof.Index)
        myMux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)   

    if err := http.ListenAndServe(":8081", myMux); err != nil {
        logFatal("Error when starting or running http server: %v", err)
    }       

}
person Naresh    schedule 15.02.2016