add debug http server to services

Currently, the debug http server in every service contains just the
net/http/pprof handlers. This allows us to get CPU, blocking, and memory
profiling remotely.

Along the way, remove all the places we use http.DefaultServeMux (which
includes use of http.Handle and http.HandlerFunc) and use a NewServeMux
for each place.

Fixes #457
This commit is contained in:
Jeff Hodges 2015-07-10 17:05:52 -07:00
parent 4a40def0f4
commit ef54dda46a
16 changed files with 125 additions and 32 deletions

View File

@ -44,6 +44,9 @@ type Config struct {
// The maximum number of subjectAltNames in a single certificate
MaxNames int
CFSSL cfsslConfig.Config
// DebugAddr is the address to run the /debug handlers on.
DebugAddr string
}
// KeyConfig should contain either a File path to a PEM-format private key,

View File

@ -148,6 +148,8 @@ func main() {
blog.SetAuditLogger(auditlogger)
go cmd.DebugServer(c.ActivityMonitor.DebugAddr)
ch, err := cmd.AmqpChannel(c)
cmd.FailOnError(err, "Could not connect to AMQP")

View File

@ -30,7 +30,10 @@ func main() {
blog.SetAuditLogger(auditlogger)
go cmd.DebugServer(c.CA.DebugAddr)
cadb, err := ca.NewCertificateAuthorityDatabaseImpl(c.CA.DBDriver, c.CA.DBConnect)
cmd.FailOnError(err, "Failed to create CA database")
if c.SQL.CreateTables {

View File

@ -31,6 +31,8 @@ func main() {
blog.SetAuditLogger(auditlogger)
go cmd.DebugServer(c.RA.DebugAddr)
rai := ra.NewRegistrationAuthorityImpl()
rai.AuthzBase = c.Common.BaseURL + wfe.AuthzPath
rai.MaxKeySize = c.Common.MaxKeySize

View File

@ -30,7 +30,10 @@ func main() {
blog.SetAuditLogger(auditlogger)
go cmd.DebugServer(c.SA.DebugAddr)
sai, err := sa.NewSQLStorageAuthority(c.SA.DBDriver, c.SA.DBConnect)
cmd.FailOnError(err, "Failed to create SA impl")
sai.SetSQLDebug(c.SQL.SQLDebug)

View File

@ -33,6 +33,8 @@ func main() {
blog.SetAuditLogger(auditlogger)
go cmd.DebugServer(c.VA.DebugAddr)
go cmd.ProfileCmd("VA", stats)
vai := va.NewValidationAuthorityImpl(c.CA.TestMode)

View File

@ -87,6 +87,8 @@ func main() {
blog.SetAuditLogger(auditlogger)
go cmd.DebugServer(c.WFE.DebugAddr)
wfe, err := wfe.NewWebFrontEndImpl()
cmd.FailOnError(err, "Unable to create WFE")
rac, sac, closeChan := setupWFE(c)
@ -118,13 +120,13 @@ func main() {
// Set up paths
wfe.BaseURL = c.Common.BaseURL
wfe.HandlePaths()
h := wfe.Handler()
auditlogger.Info(app.VersionString())
// Add HandlerTimer to output resp time + success/failure stats to statsd
auditlogger.Info(fmt.Sprintf("Server running, listening on %s...\n", c.WFE.ListenAddress))
err = http.ListenAndServe(c.WFE.ListenAddress, HandlerTimer(http.DefaultServeMux, stats))
err = http.ListenAndServe(c.WFE.ListenAddress, HandlerTimer(h, stats))
cmd.FailOnError(err, "Error starting HTTP server")
}

View File

@ -70,6 +70,8 @@ func main() {
blog.SetAuditLogger(auditlogger)
go cmd.DebugServer("localhost:8080")
// Run StatsD profiling
go cmd.ProfileCmd("Monolith", stats)
@ -120,7 +122,7 @@ func main() {
// Set up paths
ra.AuthzBase = c.Common.BaseURL + wfe.AuthzPath
wfei.BaseURL = c.Common.BaseURL
wfei.HandlePaths()
h := wfei.Handler()
ra.MaxKeySize = c.Common.MaxKeySize
ca.MaxKeySize = c.Common.MaxKeySize
@ -128,7 +130,7 @@ func main() {
auditlogger.Info(app.VersionString())
fmt.Fprintf(os.Stderr, "Server running, listening on %s...\n", c.WFE.ListenAddress)
err = http.ListenAndServe(c.WFE.ListenAddress, HandlerTimer(http.DefaultServeMux, stats))
err = http.ListenAndServe(c.WFE.ListenAddress, HandlerTimer(h, stats))
cmd.FailOnError(err, "Error starting HTTP server")
}

View File

@ -127,6 +127,8 @@ func main() {
blog.SetAuditLogger(auditlogger)
go cmd.DebugServer(c.OCSPResponder.DebugAddr)
go cmd.ProfileCmd("OCSP", stats)
auditlogger.Info(app.VersionString())
@ -148,11 +150,12 @@ func main() {
cmd.FailOnError(err, "Could not connect to OCSP database")
// Configure HTTP
http.Handle(c.OCSPResponder.Path, cfocsp.Responder{Source: src})
m := http.NewServeMux()
m.Handle(c.OCSPResponder.Path, cfocsp.Responder{Source: src})
// Add HandlerTimer to output resp time + success/failure stats to statsd
auditlogger.Info(fmt.Sprintf("Server running, listening on %s...\n", c.OCSPResponder.ListenAddress))
err = http.ListenAndServe(c.OCSPResponder.ListenAddress, HandlerTimer(http.DefaultServeMux, stats))
err = http.ListenAndServe(c.OCSPResponder.ListenAddress, HandlerTimer(m, stats))
cmd.FailOnError(err, "Error starting HTTP server")
}

View File

@ -214,6 +214,8 @@ func main() {
blog.SetAuditLogger(auditlogger)
go cmd.DebugServer(c.OCSPUpdater.DebugAddr)
// Configure DB
dbMap, err := sa.NewDbMap(c.OCSPUpdater.DBDriver, c.OCSPUpdater.DBConnect)
cmd.FailOnError(err, "Could not connect to database")

View File

@ -29,6 +29,10 @@ import (
"errors"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
_ "net/http/pprof"
"os"
"runtime"
"strings"
@ -49,6 +53,11 @@ import (
//
// Note: NO DEFAULTS are provided.
type Config struct {
ActivityMonitor struct {
// DebugAddr is the address to run the /debug handlers on.
DebugAddr string
}
// General
AMQP struct {
Server string
@ -63,19 +72,33 @@ type Config struct {
WFE struct {
BaseURL string
ListenAddress string
// DebugAddr is the address to run the /debug handlers on.
DebugAddr string
}
CA ca.Config
RA struct {
// DebugAddr is the address to run the /debug handlers on.
DebugAddr string
}
SA struct {
DBDriver string
DBConnect string
// DebugAddr is the address to run the /debug handlers on.
DebugAddr string
}
VA struct {
DNSResolver string
DNSTimeout string
UserAgent string
// DebugAddr is the address to run the /debug handlers on.
DebugAddr string
}
SQL struct {
@ -111,6 +134,9 @@ type Config struct {
DBConnect string
Path string
ListenAddress string
// DebugAddr is the address to run the /debug handlers on.
DebugAddr string
}
OCSPUpdater struct {
@ -118,6 +144,9 @@ type Config struct {
DBConnect string
MinTimeToExpiry string
ResponseLimit int
// DebugAddr is the address to run the /debug handlers on.
DebugAddr string
}
Common struct {
@ -335,3 +364,15 @@ func LoadCert(path string) (cert []byte, err error) {
cert = block.Bytes
return
}
func DebugServer(addr string) {
if addr == "" {
log.Fatalf("unable to boot debug server because no address was given for it. Set debugAddr.")
}
ln, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalf("unable to boot debug server on %#v", addr)
}
log.Printf("booting debug server at %#v", addr)
log.Println(http.Serve(ln, nil))
}

View File

@ -36,7 +36,8 @@
},
"wfe": {
"listenAddress": "127.0.0.1:4000"
"listenAddress": "127.0.0.1:4000",
"debugAddr": "localhost:8000"
},
"ca": {
@ -44,6 +45,7 @@
"profile": "ee",
"dbDriver": "sqlite3",
"dbConnect": ":memory:",
"debugAddr": "localhost:8001",
"testMode": true,
"_comment": "This should only be present in testMode. In prod use an HSM.",
"Key": {
@ -103,15 +105,21 @@
}
},
"ra": {
"debugAddr": "localhost:8002"
},
"sa": {
"dbDriver": "sqlite3",
"dbConnect": ":memory:"
"dbConnect": ":memory:",
"debugAddr": "localhost:8003"
},
"va": {
"dnsResolver": "8.8.8.8:53",
"dnsTimeout": "10s",
"userAgent": "boulder"
"userAgent": "boulder",
"debugAddr": "localhost:8004"
},
"sql": {
@ -128,13 +136,15 @@
"dbDriver": "sqlite3",
"dbConnect": ":memory:",
"path": "/",
"listenAddress": "localhost:4001"
"listenAddress": "localhost:4001",
"debugAddr": "localhost:8005"
},
"ocspUpdater": {
"dbDriver": "sqlite3",
"dbConnect": ":memory:",
"minTimeToExpiry": "72h"
"minTimeToExpiry": "72h",
"debugAddr": "localhost:8006"
},
"mail": {

View File

@ -31,7 +31,8 @@
},
"wfe": {
"listenAddress": "127.0.0.1:4000"
"listenAddress": "127.0.0.1:4000",
"debugAddr": "localhost:8000"
},
"ca": {
@ -39,6 +40,7 @@
"profile": "ee",
"dbDriver": "sqlite3",
"dbConnect": ":memory:",
"debugAddr": "localhost:8001",
"testMode": true,
"_comment": "This should only be present in testMode. In prod use an HSM.",
"expiry": "2160h",
@ -94,9 +96,14 @@
}
},
"ra": {
"debugAddr": "localhost:8002"
},
"sa": {
"dbDriver": "sqlite3",
"dbConnect": ":memory:"
"dbConnect": ":memory:",
"debugAddr": "localhost:8003"
},
"sql": {
@ -112,6 +119,7 @@
"ocspResponder": {
"dbDriver": "sqlite3",
"dbConnect": ":memory:",
"debugAddr": "localhost:8004",
"path": "/",
"listenAddress": "localhost:4001"
},
@ -119,6 +127,7 @@
"ocspUpdater": {
"dbDriver": "sqlite3",
"dbConnect": ":memory:",
"debugAddr": "localhost:8005",
"minTimeToExpiry": "72h"
},

View File

@ -31,7 +31,8 @@
},
"wfe": {
"listenAddress": "127.0.0.1:4300"
"listenAddress": "127.0.0.1:4300",
"debugAddr": "localhost:8000"
},
"ca": {
@ -39,6 +40,7 @@
"profile": "ee",
"dbDriver": "sqlite3",
"dbConnect": ":memory:",
"debugAddr": "localhost:8001",
"testMode": true,
"_comment": "This should only be present in testMode. In prod use an HSM.",
"Key": {
@ -98,15 +100,21 @@
}
},
"ra": {
"debugAddr": "localhost:8002"
},
"sa": {
"dbDriver": "sqlite3",
"dbConnect": ":memory:"
"dbConnect": ":memory:",
"debugAddr": "localhost:8003"
},
"va": {
"dnsResolver": "8.8.8.8:53",
"dnsTimeout": "10s",
"userAgent": "boulder"
"userAgent": "boulder",
"debugAddr": "localhost:8004"
},
"sql": {

View File

@ -56,10 +56,9 @@ const pathWrongToken = "wrongtoken"
const path404 = "404"
func simpleSrv(t *testing.T, token string, stopChan, waitChan chan bool, enableTLS bool) {
// Reset any existing handlers
http.DefaultServeMux = http.NewServeMux()
m := http.NewServeMux()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, path404) {
t.Logf("SIMPLESRV: Got a 404 req\n")
http.NotFound(w, r)
@ -78,7 +77,7 @@ func simpleSrv(t *testing.T, token string, stopChan, waitChan chan bool, enableT
}
})
server := &http.Server{Addr: "localhost:5001"}
server := &http.Server{Addr: "localhost:5001", Handler: m}
conn, err := net.Listen("tcp", server.Addr)
if err != nil {
waitChan <- true

View File

@ -123,7 +123,7 @@ func NewWebFrontEndImpl() (WebFrontEndImpl, error) {
// HandlePaths configures the HTTP engine to use various functions
// as methods for various ACME-specified paths.
func (wfe *WebFrontEndImpl) HandlePaths() {
func (wfe *WebFrontEndImpl) Handler() http.Handler {
wfe.NewReg = wfe.BaseURL + NewRegPath
wfe.RegBase = wfe.BaseURL + RegPath
wfe.NewAuthz = wfe.BaseURL + NewAuthzPath
@ -131,17 +131,19 @@ func (wfe *WebFrontEndImpl) HandlePaths() {
wfe.NewCert = wfe.BaseURL + NewCertPath
wfe.CertBase = wfe.BaseURL + CertPath
http.HandleFunc("/", wfe.Index)
http.HandleFunc(NewRegPath, wfe.NewRegistration)
http.HandleFunc(NewAuthzPath, wfe.NewAuthorization)
http.HandleFunc(NewCertPath, wfe.NewCertificate)
http.HandleFunc(RegPath, wfe.Registration)
http.HandleFunc(AuthzPath, wfe.Authorization)
http.HandleFunc(CertPath, wfe.Certificate)
http.HandleFunc(RevokeCertPath, wfe.RevokeCertificate)
http.HandleFunc(TermsPath, wfe.Terms)
http.HandleFunc(IssuerPath, wfe.Issuer)
http.HandleFunc(BuildIDPath, wfe.BuildID)
m := http.NewServeMux()
m.HandleFunc("/", wfe.Index)
m.HandleFunc(NewRegPath, wfe.NewRegistration)
m.HandleFunc(NewAuthzPath, wfe.NewAuthorization)
m.HandleFunc(NewCertPath, wfe.NewCertificate)
m.HandleFunc(RegPath, wfe.Registration)
m.HandleFunc(AuthzPath, wfe.Authorization)
m.HandleFunc(CertPath, wfe.Certificate)
m.HandleFunc(RevokeCertPath, wfe.RevokeCertificate)
m.HandleFunc(TermsPath, wfe.Terms)
m.HandleFunc(IssuerPath, wfe.Issuer)
m.HandleFunc(BuildIDPath, wfe.BuildID)
return m
}
// Method implementations