使用net / http / pprof用Gorilla的mux构建的Go Web应用程序性能分析

问题描述:

I have a relatively big web application written in Go that uses Gorilla's mux for routing. I recently realised that my web application is quite slow and I would like to profile the web application.

After reading about it, it seems that net/http/pprof is what I need. But I can't make it run with mux; even in the case of the most trivial web application.

Does anyone knows how to make that work?

Here is an example of a trivial code that does not work (i.e. nothing is served at /debug).

package main

import (
    "fmt"
    "github.com/gorilla/mux"
    "math"
    "net/http"
)
import _ "net/http/pprof"

func SayHello(w http.ResponseWriter, r *http.Request) {
    for i := 0; i < 1000000; i++ {
        math.Pow(36, 89)
    }
    fmt.Fprint(w, "Hello!")
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/hello", SayHello)
    http.ListenAndServe(":6060", r)
}

Sorry for that question. The answer is in the init() function of pprof. One just need to add 4 functions from pprof to the mux router. Here is the fixed code from above.

package main

import (
    "fmt"
    "github.com/gorilla/mux"
    "math"

    "net/http"
)
import "net/http/pprof"

func AttachProfiler(router *mux.Router) {
    router.HandleFunc("/debug/pprof/", pprof.Index)
    router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
    router.HandleFunc("/debug/pprof/profile", pprof.Profile)
    router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
}

func SayHello(w http.ResponseWriter, r *http.Request) {
    for i := 0; i < 1000000; i++ {
        math.Pow(36, 89)
    }
    fmt.Fprint(w, "Hello!")
}

func main() {
    r := mux.NewRouter()
    AttachProfiler(r)
    r.HandleFunc("/hello", SayHello)
    http.ListenAndServe(":6060", r)
}

user983716 - Thanks for your question and solution!

I was not able to use the links from the web index (http://[my-server]/debug/pprof), until I added a few lines to your solution, like so:

...

func AttachProfiler(router *mux.Router) {
    router.HandleFunc("/debug/pprof/", pprof.Index)
    router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
    router.HandleFunc("/debug/pprof/profile", pprof.Profile)
    router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)

    // Manually add support for paths linked to by index page at /debug/pprof/
    router.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
    router.Handle("/debug/pprof/heap", pprof.Handler("heap"))
    router.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
    router.Handle("/debug/pprof/block", pprof.Handler("block"))
}

...

If anyone has the same problem, I hope this helps!

My preferred method for this is to just let net/http/pprof register itself to http.DefaultServeMux, and then pass all requests starting with /debug/pprof/ along:

package main

import (
    "net/http"
    _ "net/http/pprof"

    "github.com/gorilla/mux"
)

func main() {
    router := mux.NewRouter()
    router.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)
    if err := http.ListenAndServe(":6060", router); err != nil {
        panic(err)
    }
}

I find that this approach is a lot more stable than one that depends on the implementation of a hidden initialization method, and also guarantees that you didn't miss anything.

I did something else, I added another native http server on a different port and it just works out of the box

package main

import (
    "fmt"
    "log"
    "net/http"

    _ "net/http/pprof"
)


func main() {
    go func() {
        log.Println(http.ListenAndServe(":6060", nil))
    }()
    log.Fatalln(http.ListenAndServe(":8080", route.Handlers()))
}

Now the pprof endpoint is at : http://localhost:6060/debug/pprof/ and the applcation is running on port :8080

Just so:

r := mux.NewRouter()
r.PathPrefix("/debug").Handler(http.DefaultServeMux)

Im using https://github.com/julienschmidt/httprouter but i just got this answer from google search. That's what i did

router := httprouter.New()
router.Handler("GET", "/debug/pprof/profile", http.DefaultServeMux)
router.Handler("GET", "/debug/pprof/heap", http.DefaultServeMux)

I only need this two routes. This answer is combine of @damien and @user983716 answers.

Previous examples not really work on my side.

To use pprof in an existing golang project with gorrila/mux, try to add :

...previous code

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/hello", SayHello)

    go func() {
        log.Fatal(http.ListenAndServe(":6061", http.DefaultServeMux))
    }()

    http.ListenAndServe(":6060", r)
}

then go to http://localhost:6061/debug/pprof/

The following should work:

import (
 "net/http"
 _ "net/http/pprof"
)

myrouter.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)