Add support for maximum connections for backends.
This commit is contained in:
parent
5c8d9f4eb9
commit
a15578a8f6
6 changed files with 101 additions and 4 deletions
|
@ -107,7 +107,7 @@ A circuit breaker can also be applied to a backend, preventing high loads on fai
|
|||
Initial state is Standby. CB observes the statistics and does not modify the request.
|
||||
In case if condition matches, CB enters Tripped state, where it responds with predefines code or redirects to another frontend.
|
||||
Once Tripped timer expires, CB enters Recovering state and resets all stats.
|
||||
In case if the condition does not match and recovery timer expries, CB enters Standby state.
|
||||
In case if the condition does not match and recovery timer expires, CB enters Standby state.
|
||||
|
||||
It can be configured using:
|
||||
|
||||
|
@ -120,6 +120,26 @@ For example:
|
|||
- `LatencyAtQuantileMS(50.0) > 50`: watch latency at quantile in milliseconds.
|
||||
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in range [500-600) to [0-600)
|
||||
|
||||
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can
|
||||
also be applied to each backend.
|
||||
|
||||
Maximum connections can be configured by specifying an integer value for `maxconn.amount` and
|
||||
`maxconn.extractorfunc` which is a strategy used to determine how to categorize requests in order to
|
||||
evaluate the maximum connections.
|
||||
|
||||
For example:
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.maxconn]
|
||||
amount = 10
|
||||
extractorfunc = "request.host"
|
||||
```
|
||||
|
||||
- `backend1` will return `HTTP code 429 Too Many Requests` if there are already 10 requests in progress for the same Host header.
|
||||
- Another possible value for `extractorfunc` is `client.ip` which will categorize requests based on client source ip.
|
||||
- Lastly `extractorfunc` can take the value of `request.header.ANY_HEADER` which will categorize requests based on `ANY_HEADER` that you provide.
|
||||
|
||||
## Servers
|
||||
|
||||
Servers are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balacning).
|
||||
|
|
|
@ -218,6 +218,9 @@ defaultEntryPoints = ["http", "https"]
|
|||
url = "http://172.17.0.3:80"
|
||||
weight = 1
|
||||
[backends.backend2]
|
||||
[backends.backend1.maxconn]
|
||||
amount = 10
|
||||
extractorfunc = "request.host"
|
||||
[backends.backend2.LoadBalancer]
|
||||
method = "drr"
|
||||
[backends.backend2.servers.server1]
|
||||
|
@ -281,6 +284,9 @@ filename = "rules.toml"
|
|||
url = "http://172.17.0.3:80"
|
||||
weight = 1
|
||||
[backends.backend2]
|
||||
[backends.backend1.maxconn]
|
||||
amount = 10
|
||||
extractorfunc = "request.host"
|
||||
[backends.backend2.LoadBalancer]
|
||||
method = "drr"
|
||||
[backends.backend2.servers.server1]
|
||||
|
@ -851,6 +857,8 @@ The Keys-Values structure should look (using `prefix = "/traefik"`):
|
|||
|
||||
| Key | Value |
|
||||
|-----------------------------------------------------|------------------------|
|
||||
| `/traefik/backends/backend2/maxconn/amount` | `10` |
|
||||
| `/traefik/backends/backend2/maxconn/extractorfunc` | `request.host` |
|
||||
| `/traefik/backends/backend2/loadbalancer/method` | `drr` |
|
||||
| `/traefik/backends/backend2/servers/server1/url` | `http://172.17.0.4:80` |
|
||||
| `/traefik/backends/backend2/servers/server1/weight` | `1` |
|
||||
|
|
|
@ -168,3 +168,41 @@ func TestReplace(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetConfigurationReturnsCorrectMaxConnConfiguration(t *testing.T) {
|
||||
templateFile, err := ioutil.TempFile("", "provider-configuration")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(templateFile.Name())
|
||||
data := []byte(`[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.maxconn]
|
||||
amount = 10
|
||||
extractorFunc = "request.host"`)
|
||||
err = ioutil.WriteFile(templateFile.Name(), data, 0700)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
provider := &myProvider{
|
||||
BaseProvider{
|
||||
Filename: templateFile.Name(),
|
||||
},
|
||||
}
|
||||
configuration, err := provider.getConfiguration(templateFile.Name(), nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Shouldn't have error out, got %v", err)
|
||||
}
|
||||
if configuration == nil {
|
||||
t.Fatalf("Configuration should not be nil, but was")
|
||||
}
|
||||
|
||||
if configuration.Backends["backend1"].MaxConn.Amount != 10 {
|
||||
t.Fatalf("Configuration did not parse MaxConn.Amount properly")
|
||||
}
|
||||
|
||||
if configuration.Backends["backend1"].MaxConn.ExtractorFunc != "request.host" {
|
||||
t.Fatalf("Configuration did not parse MaxConn.ExtractorFunc properly")
|
||||
}
|
||||
}
|
||||
|
|
14
server.go
14
server.go
|
@ -22,9 +22,11 @@ import (
|
|||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/negroni"
|
||||
"github.com/containous/oxy/cbreaker"
|
||||
"github.com/containous/oxy/connlimit"
|
||||
"github.com/containous/oxy/forward"
|
||||
"github.com/containous/oxy/roundrobin"
|
||||
"github.com/containous/oxy/stream"
|
||||
"github.com/containous/oxy/utils"
|
||||
"github.com/containous/traefik/middlewares"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/safe"
|
||||
|
@ -423,6 +425,18 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
|
|||
}
|
||||
}
|
||||
}
|
||||
maxConns := configuration.Backends[frontend.Backend].MaxConn
|
||||
if maxConns != nil && maxConns.Amount != 0 {
|
||||
extractFunc, err := utils.NewExtractor(maxConns.ExtractorFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("Creating loadd-balancer connlimit")
|
||||
lb, err = connlimit.New(lb, extractFunc, maxConns.Amount, connlimit.Logger(oxyLogger))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// retry ?
|
||||
if globalConfiguration.Retry != nil {
|
||||
retries := len(configuration.Backends[frontend.Backend].Servers)
|
||||
|
|
|
@ -17,6 +17,16 @@
|
|||
method = "{{$loadBalancer}}"
|
||||
{{end}}
|
||||
|
||||
{{$maxConnAmt := Get "" . "/maxconn/" "amount"}}
|
||||
{{$maxConnExtractorFunc := Get "" . "/maxconn/" "extractorfunc"}}
|
||||
{{with $maxConnAmt}}
|
||||
{{with $maxConnExtractorFunc}}
|
||||
[backends.{{Last $backend}}.maxConn]
|
||||
amount = {{$maxConnAmt}}
|
||||
extractorFunc = "{{$maxConnExtractorFunc}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{range $servers}}
|
||||
[backends.{{Last $backend}}.servers.{{Last .}}]
|
||||
url = "{{Get "" . "/url"}}"
|
||||
|
|
|
@ -10,6 +10,13 @@ type Backend struct {
|
|||
Servers map[string]Server `json:"servers,omitempty"`
|
||||
CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty"`
|
||||
LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty"`
|
||||
MaxConn *MaxConn `json:"maxConn,omitempty"`
|
||||
}
|
||||
|
||||
// MaxConn holds maximum connection configuraiton
|
||||
type MaxConn struct {
|
||||
Amount int64 `json:"amount,omitempty"`
|
||||
ExtractorFunc string `json:"extractorFunc,omitempty"`
|
||||
}
|
||||
|
||||
// LoadBalancer holds load balancing configuration.
|
||||
|
|
Loading…
Reference in a new issue