diff --git a/debug/profile/http/http.go b/debug/profile/http/http.go new file mode 100644 index 00000000..66cbf49c --- /dev/null +++ b/debug/profile/http/http.go @@ -0,0 +1,78 @@ +// Package http enables the http profiler +package http + +import ( + "context" + "net/http" + "net/http/pprof" + "sync" + + "github.com/micro/go-micro/debug/profile" +) + +type httpProfile struct { + sync.Mutex + running bool + server *http.Server +} + +var ( + DefaultAddress = ":6060" +) + +// Start the profiler +func (h *httpProfile) Start() error { + h.Lock() + defer h.Unlock() + + if h.running { + return nil + } + + go func() { + if err := h.server.ListenAndServe(); err != nil { + h.Lock() + h.running = false + h.Unlock() + } + }() + + h.running = true + + return nil +} + +// Stop the profiler +func (h *httpProfile) Stop() error { + h.Lock() + defer h.Unlock() + + if !h.running { + return nil + } + + h.running = false + + return h.server.Shutdown(context.TODO()) +} + +func (h *httpProfile) String() string { + return "http" +} + +func NewProfile(opts ...profile.Option) profile.Profile { + mux := http.NewServeMux() + + mux.HandleFunc("/debug/pprof/", pprof.Index) + mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + mux.HandleFunc("/debug/pprof/profile", pprof.Profile) + mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + mux.HandleFunc("/debug/pprof/trace", pprof.Trace) + + return &httpProfile{ + server: &http.Server{ + Addr: DefaultAddress, + Handler: mux, + }, + } +} diff --git a/debug/profile/pprof/pprof.go b/debug/profile/pprof/pprof.go index b1f126d9..5dfd49ba 100644 --- a/debug/profile/pprof/pprof.go +++ b/debug/profile/pprof/pprof.go @@ -107,6 +107,10 @@ func (p *profiler) Stop() error { } } +func (p *profiler) String() string { + return "pprof" +} + func NewProfile(opts ...profile.Option) profile.Profile { var options profile.Options for _, o := range opts { diff --git a/debug/profile/profile.go b/debug/profile/profile.go index 2dbe1e13..3cc075f4 100644 --- a/debug/profile/profile.go +++ b/debug/profile/profile.go @@ -6,6 +6,8 @@ type Profile interface { Start() error // Stop the profiler Stop() error + // Name of the profiler + String() string } type Options struct { diff --git a/service.go b/service.go index efddd0a4..0245bfd6 100644 --- a/service.go +++ b/service.go @@ -3,6 +3,7 @@ package micro import ( "os" "os/signal" + "runtime" "strings" "sync" "syscall" @@ -10,6 +11,7 @@ import ( "github.com/micro/go-micro/client" "github.com/micro/go-micro/config/cmd" "github.com/micro/go-micro/debug/profile" + "github.com/micro/go-micro/debug/profile/http" "github.com/micro/go-micro/debug/profile/pprof" "github.com/micro/go-micro/debug/service/handler" "github.com/micro/go-micro/plugin" @@ -151,12 +153,25 @@ func (s *service) Run() error { // start the profiler // TODO: set as an option to the service, don't just use pprof if prof := os.Getenv("MICRO_DEBUG_PROFILE"); len(prof) > 0 { - service := s.opts.Server.Options().Name - version := s.opts.Server.Options().Version - id := s.opts.Server.Options().Id - profiler := pprof.NewProfile( - profile.Name(service + "." + version + "." + id), - ) + var profiler profile.Profile + + // to view mutex contention + runtime.SetMutexProfileFraction(5) + // to view blocking profile + runtime.SetBlockProfileRate(1) + + switch prof { + case "http": + profiler = http.NewProfile() + default: + service := s.opts.Server.Options().Name + version := s.opts.Server.Options().Version + id := s.opts.Server.Options().Id + profiler = pprof.NewProfile( + profile.Name(service + "." + version + "." + id), + ) + } + if err := profiler.Start(); err != nil { return err }