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
|
@ -70,7 +70,7 @@ Frontends can be defined using the following rules:
|
||||||
- `PathPrefixStrip`: Same as `PathPrefix` but strip the given prefix from the request URL's Path.
|
- `PathPrefixStrip`: Same as `PathPrefix` but strip the given prefix from the request URL's Path.
|
||||||
|
|
||||||
You can optionally enable `passHostHeader` to forward client `Host` header to the backend.
|
You can optionally enable `passHostHeader` to forward client `Host` header to the backend.
|
||||||
|
|
||||||
Here is an example of frontends definition:
|
Here is an example of frontends definition:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
@ -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.
|
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.
|
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.
|
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:
|
It can be configured using:
|
||||||
|
|
||||||
|
@ -120,6 +120,26 @@ For example:
|
||||||
- `LatencyAtQuantileMS(50.0) > 50`: watch latency at quantile in milliseconds.
|
- `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)
|
- `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
|
||||||
|
|
||||||
Servers are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balacning).
|
Servers are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balacning).
|
||||||
|
|
12
docs/toml.md
12
docs/toml.md
|
@ -138,7 +138,7 @@
|
||||||
# storageFile = "acme.json"
|
# storageFile = "acme.json"
|
||||||
|
|
||||||
# Entrypoint to proxy acme challenge to.
|
# Entrypoint to proxy acme challenge to.
|
||||||
# WARNING, must point to an entrypoint on port 443
|
# WARNING, must point to an entrypoint on port 443
|
||||||
#
|
#
|
||||||
# Required
|
# Required
|
||||||
#
|
#
|
||||||
|
@ -218,6 +218,9 @@ defaultEntryPoints = ["http", "https"]
|
||||||
url = "http://172.17.0.3:80"
|
url = "http://172.17.0.3:80"
|
||||||
weight = 1
|
weight = 1
|
||||||
[backends.backend2]
|
[backends.backend2]
|
||||||
|
[backends.backend1.maxconn]
|
||||||
|
amount = 10
|
||||||
|
extractorfunc = "request.host"
|
||||||
[backends.backend2.LoadBalancer]
|
[backends.backend2.LoadBalancer]
|
||||||
method = "drr"
|
method = "drr"
|
||||||
[backends.backend2.servers.server1]
|
[backends.backend2.servers.server1]
|
||||||
|
@ -281,6 +284,9 @@ filename = "rules.toml"
|
||||||
url = "http://172.17.0.3:80"
|
url = "http://172.17.0.3:80"
|
||||||
weight = 1
|
weight = 1
|
||||||
[backends.backend2]
|
[backends.backend2]
|
||||||
|
[backends.backend1.maxconn]
|
||||||
|
amount = 10
|
||||||
|
extractorfunc = "request.host"
|
||||||
[backends.backend2.LoadBalancer]
|
[backends.backend2.LoadBalancer]
|
||||||
method = "drr"
|
method = "drr"
|
||||||
[backends.backend2.servers.server1]
|
[backends.backend2.servers.server1]
|
||||||
|
@ -851,6 +857,8 @@ The Keys-Values structure should look (using `prefix = "/traefik"`):
|
||||||
|
|
||||||
| Key | Value |
|
| Key | Value |
|
||||||
|-----------------------------------------------------|------------------------|
|
|-----------------------------------------------------|------------------------|
|
||||||
|
| `/traefik/backends/backend2/maxconn/amount` | `10` |
|
||||||
|
| `/traefik/backends/backend2/maxconn/extractorfunc` | `request.host` |
|
||||||
| `/traefik/backends/backend2/loadbalancer/method` | `drr` |
|
| `/traefik/backends/backend2/loadbalancer/method` | `drr` |
|
||||||
| `/traefik/backends/backend2/servers/server1/url` | `http://172.17.0.4:80` |
|
| `/traefik/backends/backend2/servers/server1/url` | `http://172.17.0.4:80` |
|
||||||
| `/traefik/backends/backend2/servers/server1/weight` | `1` |
|
| `/traefik/backends/backend2/servers/server1/weight` | `1` |
|
||||||
|
@ -1006,4 +1014,4 @@ entryPoint = "https"
|
||||||
entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
||||||
backend = "backend2"
|
backend = "backend2"
|
||||||
rule = "Path:/test"
|
rule = "Path:/test"
|
||||||
```
|
```
|
||||||
|
|
|
@ -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"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/negroni"
|
"github.com/codegangsta/negroni"
|
||||||
"github.com/containous/oxy/cbreaker"
|
"github.com/containous/oxy/cbreaker"
|
||||||
|
"github.com/containous/oxy/connlimit"
|
||||||
"github.com/containous/oxy/forward"
|
"github.com/containous/oxy/forward"
|
||||||
"github.com/containous/oxy/roundrobin"
|
"github.com/containous/oxy/roundrobin"
|
||||||
"github.com/containous/oxy/stream"
|
"github.com/containous/oxy/stream"
|
||||||
|
"github.com/containous/oxy/utils"
|
||||||
"github.com/containous/traefik/middlewares"
|
"github.com/containous/traefik/middlewares"
|
||||||
"github.com/containous/traefik/provider"
|
"github.com/containous/traefik/provider"
|
||||||
"github.com/containous/traefik/safe"
|
"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 ?
|
// retry ?
|
||||||
if globalConfiguration.Retry != nil {
|
if globalConfiguration.Retry != nil {
|
||||||
retries := len(configuration.Backends[frontend.Backend].Servers)
|
retries := len(configuration.Backends[frontend.Backend].Servers)
|
||||||
|
|
|
@ -17,6 +17,16 @@
|
||||||
method = "{{$loadBalancer}}"
|
method = "{{$loadBalancer}}"
|
||||||
{{end}}
|
{{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}}
|
{{range $servers}}
|
||||||
[backends.{{Last $backend}}.servers.{{Last .}}]
|
[backends.{{Last $backend}}.servers.{{Last .}}]
|
||||||
url = "{{Get "" . "/url"}}"
|
url = "{{Get "" . "/url"}}"
|
||||||
|
|
|
@ -10,6 +10,13 @@ type Backend struct {
|
||||||
Servers map[string]Server `json:"servers,omitempty"`
|
Servers map[string]Server `json:"servers,omitempty"`
|
||||||
CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty"`
|
CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty"`
|
||||||
LoadBalancer *LoadBalancer `json:"loadBalancer,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.
|
// LoadBalancer holds load balancing configuration.
|
||||||
|
|
Loading…
Reference in a new issue