Skip to content

Instantly share code, notes, and snippets.

@vardius
Last active June 12, 2020 23:18
Show Gist options
  • Save vardius/c7861a3ee19cd04164165cfb5f2f95cc to your computer and use it in GitHub Desktop.
Save vardius/c7861a3ee19cd04164165cfb5f2f95cc to your computer and use it in GitHub Desktop.
Profiling Go HTTP service with pprof and expvar
package main
import (
"context"
"expvar" // Register the expvar handlers
"fmt"
"log"
"net/http"
_ "net/http/pprof" // Register the pprof handlers
"os"
"runtime"
"time"
"github.com/vardius/shutdown"
)
// m contains the global program counters for the application.
var m = struct {
gr *expvar.Int
req *expvar.Int
}{
gr: expvar.NewInt("goroutines"),
req: expvar.NewInt("requests"),
}
// Metrics updates program counters.
func Metrics(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
// Increment the request counter.
m.req.Add(1)
// Update the count for the number of active goroutines every 100 requests.
if m.req.Value()%100 == 0 {
m.gr.Set(int64(runtime.NumGoroutine()))
}
}
return http.HandlerFunc(fn)
}
type DebugServer struct {
*http.Server
}
// NewDebugServer provides new debug http server
func NewDebugServer(address string) *DebugServer {
return &DebugServer{
&http.Server{
Addr: address,
Handler: http.DefaultServeMux,
},
}
}
var isDebug = os.Getenv("DEBUG") != ""
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
})
httpServer := &http.Server{
Addr: ":8080",
Handler: Metrics(mux), // wrap our server with metrics middleware
}
go func() {
log.Printf("Server HTTP listen on %s:%d", "localhost", 8080)
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal(fmt.Sprintf("ListenAndServe: %s", err))
}
}()
var debugServer *DebugServer
if isDebug {
debugServer = NewDebugServer(fmt.Sprintf("%s:%d", "localhost", 6060))
go func() {
log.Printf("Server HTTP listen on %s:%d", "localhost", 6060)
if err := debugServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal(fmt.Sprintf("ListenAndServe: %s", err))
}
}()
}
stop := func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := httpServer.Shutdown(ctx); err != nil {
log.Fatalf("Server shutdown failed %s", err)
}
if debugServer != nil {
if err := debugServer.Shutdown(ctx); err != nil {
log.Fatalf("Debug server shutdown failed %s", err)
}
}
}
shutdown.GracefulStop(stop) // will block until shutdown signal is received
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment