Created
February 28, 2014 02:01
Revisions
-
rcrowley created this gist
Feb 28, 2014 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,117 @@ Benchmark of two graceful stop implementations ============================================== The server for each of these benchmarks is based on <https://github.com/rcrowley/go-tigertonic/tree/master/example>. It was run as `./example >/dev/null 2>/dev/null`. The client for each of these benchmarks is `ab -H"Host: example.com" -c"100" -n"1000000" "http://127.0.0.1:8000/stuff/ID"`. Baseline -------- As a baseline, I removed the `chan struct{}` and `sync.WaitGroup` from [`server.go`](https://github.com/rcrowley/go-tigertonic/blob/master/server.go) and built the binary against Go tip. ``` Concurrency Level: 100 Time taken for tests: 235.452 seconds Complete requests: 1000000 Failed requests: 0 Write errors: 0 Total transferred: 136000000 bytes HTML transferred: 28000000 bytes Requests per second: 4247.15 [#/sec] (mean) Time per request: 23.545 [ms] (mean) Time per request: 0.235 [ms] (mean, across all concurrent requests) Transfer rate: 564.07 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.7 0 18 Processing: 0 23 8.5 21 252 Waiting: 0 23 8.5 21 252 Total: 0 24 8.5 22 252 Percentage of the requests served within a certain time (ms) 50% 22 66% 23 75% 25 80% 26 90% 30 95% 37 98% 47 99% 62 100% 252 (longest request) ``` CL 67730046 ----------- This benchmark is of CL 67730046 that adds an `int32` (accessed via `sync/atomic`) and a `sync.WaitGroup` to `net/http` and uses `bufio.Buffer.Peek` to preempt keepalive connections. ``` Concurrency Level: 100 Time taken for tests: 228.575 seconds Complete requests: 1000000 Failed requests: 0 Write errors: 0 Total transferred: 136000000 bytes HTML transferred: 28000000 bytes Requests per second: 4374.94 [#/sec] (mean) Time per request: 22.857 [ms] (mean) Time per request: 0.229 [ms] (mean, across all concurrent requests) Transfer rate: 581.05 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.6 0 17 Processing: 1 23 7.9 21 209 Waiting: 1 22 7.8 21 209 Total: 1 23 7.9 21 209 Percentage of the requests served within a certain time (ms) 50% 21 66% 23 75% 24 80% 25 90% 28 95% 34 98% 45 99% 57 100% 209 (longest request) ``` CL 69260044 ----------- This benchmark is of CL 69260044 and uses `http.Server.ConnState` to manage a `chan struct{}`, `sync.WaitGroup`, and a `map[string]net.Conn` to preempt keepalive connections. ``` Concurrency Level: 100 Time taken for tests: 249.333 seconds Complete requests: 1000000 Failed requests: 0 Write errors: 0 Total transferred: 136000000 bytes HTML transferred: 28000000 bytes Requests per second: 4010.70 [#/sec] (mean) Time per request: 24.933 [ms] (mean) Time per request: 0.249 [ms] (mean, across all concurrent requests) Transfer rate: 532.67 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.8 0 70 Processing: 0 25 9.8 23 384 Waiting: 0 24 9.8 22 384 Total: 0 25 9.9 23 384 Percentage of the requests served within a certain time (ms) 50% 23 66% 25 75% 26 80% 27 90% 31 95% 40 98% 51 99% 65 100% 384 (longest request) ``` This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,149 @@ package tigertonic import ( "crypto/tls" "crypto/x509" "io/ioutil" "net" "net/http" ) // Server is an http.Server with better defaults. type Server struct { http.Server listener net.Listener } // NewServer returns an http.Server with better defaults. func NewServer(addr string, handler http.Handler) *Server { s := &Server{ Server: http.Server{ Addr: addr, Handler: &serverHandler{ Handler: handler, //ch: ch, }, MaxHeaderBytes: 4096, ReadTimeout: 60e9, // These are absolute times which must be WriteTimeout: 60e9, // longer than the longest {up,down}load. }, } return s } // NewTLSServer returns an http.Server with better defaults configured to use // the certificate and private key files. func NewTLSServer( addr, cert, key string, handler http.Handler, ) (*Server, error) { s := NewServer(addr, handler) return s, s.TLS(cert, key) } // CA overrides the certificate authority on the server's TLSConfig field. func (s *Server) CA(ca string) error { certPool := x509.NewCertPool() buf, err := ioutil.ReadFile(ca) if nil != err { return err } certPool.AppendCertsFromPEM(buf) s.tlsConfig() s.TLSConfig.RootCAs = certPool return nil } // ClientCA configures the CA pool for verifying client side certificates. func (s *Server) ClientCA(ca string) error { certPool := x509.NewCertPool() buf, err := ioutil.ReadFile(ca) if nil != err { return err } certPool.AppendCertsFromPEM(buf) s.tlsConfig() s.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert s.TLSConfig.ClientCAs = certPool return nil } // Close closes the listener the server is using and signals open connections // to close at their earliest convenience. Then it waits for all open // connections to become closed. func (s *Server) Close() error { return s.Server.Close() } // ListenAndServe calls net.Listen with s.Addr and then calls s.Serve. func (s *Server) ListenAndServe() error { addr := s.Addr if "" == addr { if nil == s.TLSConfig { addr = ":http" } else { addr = ":https" } } l, err := net.Listen("tcp", addr) if nil != err { return err } if nil != s.TLSConfig { l = tls.NewListener(l, s.TLSConfig) } return s.Serve(l) } // ListenAndServeTLS calls s.TLS with the given certificate and private key // files and then calls s.ListenAndServe. func (s *Server) ListenAndServeTLS(cert, key string) error { s.TLS(cert, key) return s.ListenAndServe() } // Serve behaves like http.Server.Serve with the added option to stop the // server gracefully with the s.Close method. func (s *Server) Serve(l net.Listener) error { s.listener = l return s.Server.Serve(s.listener) } // TLS configures this server to be a TLS server using the given certificate // and private key files. func (s *Server) TLS(cert, key string) error { c, err := tls.LoadX509KeyPair(cert, key) if nil != err { return err } s.tlsConfig() s.TLSConfig.Certificates = []tls.Certificate{c} return nil } func (s *Server) tlsConfig() { if nil == s.TLSConfig { s.TLSConfig = &tls.Config{ CipherSuites: []uint16{ tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, tls.TLS_RSA_WITH_RC4_128_SHA, }, NextProtos: []string{"http/1.1"}, } } } type serverHandler struct { http.Handler } func (h *serverHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // r.Header.Set("Host", r.Host) // Should I? r.URL.Host = r.Host if nil != r.TLS { r.URL.Scheme = "https" } else { r.URL.Scheme = "http" } h.Handler.ServeHTTP(w, r) } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,187 @@ package tigertonic import ( "crypto/tls" "crypto/x509" "io/ioutil" "net" "net/http" "sync" ) // Server is an http.Server with better defaults. type Server struct { http.Server ch chan<- struct{} conns map[string]net.Conn listener net.Listener mu sync.Mutex // guards conns waitGroup sync.WaitGroup } // NewServer returns an http.Server with better defaults. func NewServer(addr string, handler http.Handler) *Server { ch := make(chan struct{}) s := &Server{ Server: http.Server{ Addr: addr, Handler: &serverHandler{ Handler: handler, }, MaxHeaderBytes: 4096, ReadTimeout: 60e9, // These are absolute times which must be WriteTimeout: 60e9, // longer than the longest {up,down}load. }, ch: ch, conns: make(map[string]net.Conn), } s.ConnState = func(conn net.Conn, state http.ConnState) { switch state { case http.StateNew: s.waitGroup.Add(1) case http.StateActive: s.mu.Lock() delete(s.conns, conn.LocalAddr().String()) s.mu.Unlock() case http.StateIdle: select { case <-ch: conn.Close() default: s.mu.Lock() s.conns[conn.LocalAddr().String()] = conn s.mu.Unlock() } case http.StateHijacked, http.StateClosed: s.waitGroup.Done() } } return s } // NewTLSServer returns an http.Server with better defaults configured to use // the certificate and private key files. func NewTLSServer( addr, cert, key string, handler http.Handler, ) (*Server, error) { s := NewServer(addr, handler) return s, s.TLS(cert, key) } // CA overrides the certificate authority on the server's TLSConfig field. func (s *Server) CA(ca string) error { certPool := x509.NewCertPool() buf, err := ioutil.ReadFile(ca) if nil != err { return err } certPool.AppendCertsFromPEM(buf) s.tlsConfig() s.TLSConfig.RootCAs = certPool return nil } // ClientCA configures the CA pool for verifying client side certificates. func (s *Server) ClientCA(ca string) error { certPool := x509.NewCertPool() buf, err := ioutil.ReadFile(ca) if nil != err { return err } certPool.AppendCertsFromPEM(buf) s.tlsConfig() s.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert s.TLSConfig.ClientCAs = certPool return nil } // Close closes the listener the server is using and signals open connections // to close at their earliest convenience. Then it waits for all open // connections to become closed. func (s *Server) Close() error { close(s.ch) if err := s.listener.Close(); nil != err { return err } s.mu.Lock() for _, conn := range s.conns { conn.Close() } s.mu.Unlock() s.waitGroup.Wait() return nil } // ListenAndServe calls net.Listen with s.Addr and then calls s.Serve. func (s *Server) ListenAndServe() error { addr := s.Addr if "" == addr { if nil == s.TLSConfig { addr = ":http" } else { addr = ":https" } } l, err := net.Listen("tcp", addr) if nil != err { return err } if nil != s.TLSConfig { l = tls.NewListener(l, s.TLSConfig) } return s.Serve(l) } // ListenAndServeTLS calls s.TLS with the given certificate and private key // files and then calls s.ListenAndServe. func (s *Server) ListenAndServeTLS(cert, key string) error { s.TLS(cert, key) return s.ListenAndServe() } // Serve behaves like http.Server.Serve with the added option to stop the // server gracefully with the s.Close method. func (s *Server) Serve(l net.Listener) error { s.listener = l return s.Server.Serve(s.listener) } // TLS configures this server to be a TLS server using the given certificate // and private key files. func (s *Server) TLS(cert, key string) error { c, err := tls.LoadX509KeyPair(cert, key) if nil != err { return err } s.tlsConfig() s.TLSConfig.Certificates = []tls.Certificate{c} return nil } func (s *Server) tlsConfig() { if nil == s.TLSConfig { s.TLSConfig = &tls.Config{ CipherSuites: []uint16{ tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, tls.TLS_RSA_WITH_RC4_128_SHA, }, NextProtos: []string{"http/1.1"}, } } } type serverHandler struct { http.Handler } func (h *serverHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // r.Header.Set("Host", r.Host) // Should I? r.URL.Host = r.Host if nil != r.TLS { r.URL.Scheme = "https" } else { r.URL.Scheme = "http" } h.Handler.ServeHTTP(w, r) } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,149 @@ package tigertonic import ( "crypto/tls" "crypto/x509" "io/ioutil" "net" "net/http" ) // Server is an http.Server with better defaults. type Server struct { http.Server listener net.Listener } // NewServer returns an http.Server with better defaults. func NewServer(addr string, handler http.Handler) *Server { s := &Server{ Server: http.Server{ Addr: addr, Handler: &serverHandler{ Handler: handler, }, MaxHeaderBytes: 4096, ReadTimeout: 60e9, // These are absolute times which must be WriteTimeout: 60e9, // longer than the longest {up,down}load. }, } return s } // NewTLSServer returns an http.Server with better defaults configured to use // the certificate and private key files. func NewTLSServer( addr, cert, key string, handler http.Handler, ) (*Server, error) { s := NewServer(addr, handler) return s, s.TLS(cert, key) } // CA overrides the certificate authority on the server's TLSConfig field. func (s *Server) CA(ca string) error { certPool := x509.NewCertPool() buf, err := ioutil.ReadFile(ca) if nil != err { return err } certPool.AppendCertsFromPEM(buf) s.tlsConfig() s.TLSConfig.RootCAs = certPool return nil } // ClientCA configures the CA pool for verifying client side certificates. func (s *Server) ClientCA(ca string) error { certPool := x509.NewCertPool() buf, err := ioutil.ReadFile(ca) if nil != err { return err } certPool.AppendCertsFromPEM(buf) s.tlsConfig() s.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert s.TLSConfig.ClientCAs = certPool return nil } // Close closes the listener the server is using and signals open connections // to close at their earliest convenience. Then it waits for all open // connections to become closed. func (s *Server) Close() error { return nil } // ListenAndServe calls net.Listen with s.Addr and then calls s.Serve. func (s *Server) ListenAndServe() error { addr := s.Addr if "" == addr { if nil == s.TLSConfig { addr = ":http" } else { addr = ":https" } } l, err := net.Listen("tcp", addr) if nil != err { return err } if nil != s.TLSConfig { l = tls.NewListener(l, s.TLSConfig) } return s.Serve(l) } // ListenAndServeTLS calls s.TLS with the given certificate and private key // files and then calls s.ListenAndServe. func (s *Server) ListenAndServeTLS(cert, key string) error { s.TLS(cert, key) return s.ListenAndServe() } // Serve behaves like http.Server.Serve with the added option to stop the // server gracefully with the s.Close method. func (s *Server) Serve(l net.Listener) error { s.listener = l return s.Server.Serve(s.listener) } // TLS configures this server to be a TLS server using the given certificate // and private key files. func (s *Server) TLS(cert, key string) error { c, err := tls.LoadX509KeyPair(cert, key) if nil != err { return err } s.tlsConfig() s.TLSConfig.Certificates = []tls.Certificate{c} return nil } func (s *Server) tlsConfig() { if nil == s.TLSConfig { s.TLSConfig = &tls.Config{ CipherSuites: []uint16{ tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, tls.TLS_RSA_WITH_RC4_128_SHA, }, NextProtos: []string{"http/1.1"}, } } } type serverHandler struct { http.Handler //ch <-chan struct{} } func (h *serverHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // r.Header.Set("Host", r.Host) // Should I? r.URL.Host = r.Host if nil != r.TLS { r.URL.Scheme = "https" } else { r.URL.Scheme = "http" } h.Handler.ServeHTTP(w, r) }