mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-26 13:43:47 +03:00
Performance testing Load function
This commit is contained in:
parent
86e2d1b0a4
commit
a8dc73b4d9
7 changed files with 132 additions and 12 deletions
37
admin.go
37
admin.go
|
@ -1,6 +1,7 @@
|
||||||
package caddy2
|
package caddy2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -8,8 +9,10 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/pprof"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -30,6 +33,14 @@ func StartAdmin(addr string) error {
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.HandleFunc("/load", handleLoadConfig)
|
mux.HandleFunc("/load", handleLoadConfig)
|
||||||
|
|
||||||
|
///// BEGIN PPROF STUFF //////
|
||||||
|
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)
|
||||||
|
///// END PPROF STUFF //////
|
||||||
|
|
||||||
for _, m := range GetModules("admin") {
|
for _, m := range GetModules("admin") {
|
||||||
moduleValue, err := m.New()
|
moduleValue, err := m.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -40,7 +51,11 @@ func StartAdmin(addr string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
cfgEndptSrv = &http.Server{
|
cfgEndptSrv = &http.Server{
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
|
ReadTimeout: 5 * time.Second,
|
||||||
|
ReadHeaderTimeout: 5 * time.Second,
|
||||||
|
IdleTimeout: 5 * time.Second,
|
||||||
|
MaxHeaderBytes: 1024 * 256,
|
||||||
}
|
}
|
||||||
|
|
||||||
go cfgEndptSrv.Serve(ln)
|
go cfgEndptSrv.Serve(ln)
|
||||||
|
@ -74,6 +89,7 @@ type AdminRoute struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadConfig(w http.ResponseWriter, r *http.Request) {
|
func handleLoadConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
|
r.Close = true
|
||||||
if r.Method != "POST" {
|
if r.Method != "POST" {
|
||||||
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
|
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
|
@ -94,14 +110,31 @@ func handleLoadConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Load loads and starts a configuration.
|
// Load loads and starts a configuration.
|
||||||
func Load(r io.Reader) error {
|
func Load(r io.Reader) error {
|
||||||
|
buf := bufPool.Get().(*bytes.Buffer)
|
||||||
|
buf.Reset()
|
||||||
|
defer bufPool.Put(buf)
|
||||||
|
|
||||||
|
_, err := io.Copy(buf, io.LimitReader(r, 1024*1024))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var cfg Config
|
var cfg Config
|
||||||
err := json.NewDecoder(r).Decode(&cfg)
|
err = json.Unmarshal(buf.Bytes(), &cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("decoding config: %v", err)
|
return fmt.Errorf("decoding config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = Start(cfg)
|
err = Start(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("starting: %v", err)
|
return fmt.Errorf("starting: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bufPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new(bytes.Buffer)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
30
admin_test.go
Normal file
30
admin_test.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package caddy2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkLoad(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r := strings.NewReader(`{
|
||||||
|
"testval": "Yippee!",
|
||||||
|
"modules": {
|
||||||
|
"http": {
|
||||||
|
"servers": {
|
||||||
|
"myserver": {
|
||||||
|
"listen": ["tcp/localhost:8080-8084"],
|
||||||
|
"read_timeout": "30s"
|
||||||
|
},
|
||||||
|
"yourserver": {
|
||||||
|
"listen": ["127.0.0.1:5000"],
|
||||||
|
"read_header_timeout": "15s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
Load(r)
|
||||||
|
}
|
||||||
|
}
|
23
caddy.go
23
caddy.go
|
@ -3,10 +3,16 @@ package caddy2
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var currentCfg *Config
|
||||||
|
var currentCfgMu sync.Mutex
|
||||||
|
|
||||||
// Start runs Caddy with the given config.
|
// Start runs Caddy with the given config.
|
||||||
func Start(cfg Config) error {
|
func Start(cfg Config) error {
|
||||||
cfg.runners = make(map[string]Runner)
|
cfg.runners = make(map[string]Runner)
|
||||||
|
@ -26,16 +32,33 @@ func Start(cfg Config) error {
|
||||||
for name, r := range cfg.runners {
|
for name, r := range cfg.runners {
|
||||||
err := r.Run()
|
err := r.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// TODO: If any one has an error, stop the others
|
||||||
return fmt.Errorf("%s module: %v", name, err)
|
return fmt.Errorf("%s module: %v", name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentCfgMu.Lock()
|
||||||
|
if currentCfg != nil {
|
||||||
|
for _, r := range cfg.runners {
|
||||||
|
err := r.Cancel()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentCfg = &cfg
|
||||||
|
currentCfgMu.Unlock()
|
||||||
|
|
||||||
|
// TODO: debugging memory leak...
|
||||||
|
debug.FreeOSMemory()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runner is a thing that Caddy runs.
|
// Runner is a thing that Caddy runs.
|
||||||
type Runner interface {
|
type Runner interface {
|
||||||
Run() error
|
Run() error
|
||||||
|
Cancel() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config represents a Caddy configuration.
|
// Config represents a Caddy configuration.
|
||||||
|
|
|
@ -5,17 +5,19 @@ import (
|
||||||
|
|
||||||
"bitbucket.org/lightcodelabs/caddy2"
|
"bitbucket.org/lightcodelabs/caddy2"
|
||||||
|
|
||||||
|
_ "net/http/pprof"
|
||||||
|
|
||||||
// this is where modules get plugged in
|
// this is where modules get plugged in
|
||||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp"
|
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp"
|
||||||
_ "bitbucket.org/lightcodelabs/dynamicconfig"
|
_ "bitbucket.org/lightcodelabs/dynamicconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := caddy2.Start("127.0.0.1:1234")
|
err := caddy2.StartAdmin("127.0.0.1:1234")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
defer caddy2.Stop()
|
defer caddy2.StopAdmin()
|
||||||
|
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
|
19
listeners.go
19
listeners.go
|
@ -3,15 +3,29 @@ package caddy2
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Listen returns a listener suitable for use in a Caddy module.
|
// Listen returns a listener suitable for use in a Caddy module.
|
||||||
func Listen(proto, addr string) (net.Listener, error) {
|
func Listen(proto, addr string) (net.Listener, error) {
|
||||||
|
lnKey := proto + "/" + addr
|
||||||
|
|
||||||
|
listenersMu.Lock()
|
||||||
|
defer listenersMu.Unlock()
|
||||||
|
|
||||||
|
// if listener already exists, return it
|
||||||
|
if ln, ok := listeners[lnKey]; ok {
|
||||||
|
return &fakeCloseListener{Listener: ln}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// or, create new one and save it
|
||||||
ln, err := net.Listen(proto, addr)
|
ln, err := net.Listen(proto, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
listeners[lnKey] = ln
|
||||||
|
|
||||||
return &fakeCloseListener{Listener: ln}, nil
|
return &fakeCloseListener{Listener: ln}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,3 +63,8 @@ func (fcl *fakeCloseListener) CloseUnderlying() error {
|
||||||
// Close() is called, indicating that it is pretending to
|
// Close() is called, indicating that it is pretending to
|
||||||
// be closed so that the server using it can terminate.
|
// be closed so that the server using it can terminate.
|
||||||
var ErrSwappingServers = fmt.Errorf("listener 'closed' 😉")
|
var ErrSwappingServers = fmt.Errorf("listener 'closed' 😉")
|
||||||
|
|
||||||
|
var (
|
||||||
|
listeners = make(map[string]net.Listener)
|
||||||
|
listenersMu sync.Mutex
|
||||||
|
)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package caddyhttp
|
package caddyhttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
@ -24,13 +25,15 @@ func init() {
|
||||||
|
|
||||||
type httpModuleConfig struct {
|
type httpModuleConfig struct {
|
||||||
Servers map[string]httpServerConfig `json:"servers"`
|
Servers map[string]httpServerConfig `json:"servers"`
|
||||||
|
|
||||||
|
servers []*http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *httpModuleConfig) Run() error {
|
func (hc *httpModuleConfig) Run() error {
|
||||||
fmt.Printf("RUNNING: %#v\n", hc)
|
// fmt.Printf("RUNNING: %#v\n", hc)
|
||||||
|
|
||||||
for _, srv := range hc.Servers {
|
for _, srv := range hc.Servers {
|
||||||
s := http.Server{
|
s := &http.Server{
|
||||||
ReadTimeout: time.Duration(srv.ReadTimeout),
|
ReadTimeout: time.Duration(srv.ReadTimeout),
|
||||||
ReadHeaderTimeout: time.Duration(srv.ReadHeaderTimeout),
|
ReadHeaderTimeout: time.Duration(srv.ReadHeaderTimeout),
|
||||||
}
|
}
|
||||||
|
@ -53,11 +56,21 @@ func (hc *httpModuleConfig) Run() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hc *httpModuleConfig) Cancel() error {
|
||||||
|
for _, s := range hc.servers {
|
||||||
|
err := s.Shutdown(context.Background()) // TODO
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseListenAddr(a string) (proto string, addrs []string, err error) {
|
func parseListenAddr(a string) (proto string, addrs []string, err error) {
|
||||||
proto = "tcp"
|
proto = "tcp"
|
||||||
if idx := strings.Index(a, ":::"); idx >= 0 {
|
if idx := strings.Index(a, "/"); idx >= 0 {
|
||||||
proto = strings.ToLower(strings.TrimSpace(a[:idx]))
|
proto = strings.ToLower(strings.TrimSpace(a[:idx]))
|
||||||
a = a[idx+3:]
|
a = a[idx+1:]
|
||||||
}
|
}
|
||||||
var host, port string
|
var host, port string
|
||||||
host, port, err = net.SplitHostPort(a)
|
host, port, err = net.SplitHostPort(a)
|
||||||
|
|
|
@ -28,22 +28,22 @@ func TestParseListenerAddr(t *testing.T) {
|
||||||
expectAddrs: []string{":1234"},
|
expectAddrs: []string{":1234"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "tcp::::1234",
|
input: "tcp/:1234",
|
||||||
expectProto: "tcp",
|
expectProto: "tcp",
|
||||||
expectAddrs: []string{":1234"},
|
expectAddrs: []string{":1234"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "tcp6::::1234",
|
input: "tcp6/:1234",
|
||||||
expectProto: "tcp6",
|
expectProto: "tcp6",
|
||||||
expectAddrs: []string{":1234"},
|
expectAddrs: []string{":1234"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "tcp4:::localhost:1234",
|
input: "tcp4/localhost:1234",
|
||||||
expectProto: "tcp4",
|
expectProto: "tcp4",
|
||||||
expectAddrs: []string{"localhost:1234"},
|
expectAddrs: []string{"localhost:1234"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "unix:::localhost:1234-1236",
|
input: "unix/localhost:1234-1236",
|
||||||
expectProto: "unix",
|
expectProto: "unix",
|
||||||
expectAddrs: []string{"localhost:1234", "localhost:1235", "localhost:1236"},
|
expectAddrs: []string{"localhost:1234", "localhost:1235", "localhost:1236"},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue