feat(#733)Support nginx bandwidth control

This commit is contained in:
zhengjiajin 2017-08-13 14:52:20 +08:00
parent 36cf018a71
commit 890c57f2ca
6 changed files with 84 additions and 12 deletions

View file

@ -387,6 +387,8 @@ func NewDefault() Configuration {
CustomHTTPErrors: []int{},
WhitelistSourceRange: []string{},
SkipAccessLogURLs: []string{},
LimitRate: 0,
LimitRateAfter: 0,
},
UpstreamKeepaliveConnections: 0,
LimitConnZoneVariable: defaultLimitConnZoneVariable,

View file

@ -404,6 +404,18 @@ func buildRateLimit(input interface{}) []string {
limits = append(limits, limit)
}
if loc.RateLimit.LimitRateAfter > 0 {
limit := fmt.Sprintf("limit_rate_after %vk;",
loc.RateLimit.LimitRateAfter)
limits = append(limits, limit)
}
if loc.RateLimit.LimitRate > 0 {
limit := fmt.Sprintf("limit_rate %vk;",
loc.RateLimit.LimitRate)
limits = append(limits, limit)
}
return limits
}

View file

@ -22,12 +22,15 @@ import (
extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
"k8s.io/ingress/core/pkg/ingress/resolver"
)
const (
limitIP = "ingress.kubernetes.io/limit-connections"
limitRPS = "ingress.kubernetes.io/limit-rps"
limitRPM = "ingress.kubernetes.io/limit-rpm"
limitIP = "ingress.kubernetes.io/limit-connections"
limitRPS = "ingress.kubernetes.io/limit-rps"
limitRPM = "ingress.kubernetes.io/limit-rpm"
limitRATE = "ingress.kubernetes.io/limit-rate"
limitRATEAFTER = "ingress.kubernetes.io/limit-rate-after"
// allow 5 times the specified limit as burst
defBurst = 5
@ -48,6 +51,10 @@ type RateLimit struct {
RPS Zone `json:"rps"`
RPM Zone `json:"rpm"`
LimitRate int `json:"limit-rate"`
LimitRateAfter int `json:"limit-rate-after"`
}
// Equal tests for equality between two RateLimit types
@ -67,6 +74,12 @@ func (rt1 *RateLimit) Equal(rt2 *RateLimit) bool {
if !(&rt1.RPS).Equal(&rt2.RPS) {
return false
}
if rt1.LimitRate != rt2.LimitRate {
return false
}
if rt1.LimitRateAfter != rt2.LimitRateAfter {
return false
}
return true
}
@ -106,16 +119,26 @@ func (z1 *Zone) Equal(z2 *Zone) bool {
}
type ratelimit struct {
backendResolver resolver.DefaultBackend
}
// NewParser creates a new ratelimit annotation parser
func NewParser() parser.IngressAnnotation {
return ratelimit{}
func NewParser(br resolver.DefaultBackend) parser.IngressAnnotation {
return ratelimit{br}
}
// ParseAnnotations parses the annotations contained in the ingress
// rule used to rewrite the defined paths
func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
defBackend := a.backendResolver.GetDefaultBackend()
lr, err := parser.GetIntAnnotation(limitRATE, ing)
if err != nil {
lr = defBackend.LimitRate
}
lra, err := parser.GetIntAnnotation(limitRATEAFTER, ing)
if err != nil {
lra = defBackend.LimitRateAfter
}
rpm, _ := parser.GetIntAnnotation(limitRPM, ing)
rps, _ := parser.GetIntAnnotation(limitRPS, ing)
@ -123,9 +146,11 @@ func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
if rpm == 0 && rps == 0 && conn == 0 {
return &RateLimit{
Connections: Zone{},
RPS: Zone{},
RPM: Zone{},
Connections: Zone{},
RPS: Zone{},
RPM: Zone{},
LimitRate: lr,
LimitRateAfter: lra,
}, nil
}
@ -150,5 +175,7 @@ func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
Burst: rpm * defBurst,
SharedSize: defSharedSize,
},
LimitRate: lr,
LimitRateAfter: lra,
}, nil
}

View file

@ -24,6 +24,7 @@ import (
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress/core/pkg/ingress/defaults"
)
func buildIngress() *extensions.Ingress {
@ -61,9 +62,19 @@ func buildIngress() *extensions.Ingress {
}
}
type mockBackend struct {
}
func (m mockBackend) GetDefaultBackend() defaults.Backend {
return defaults.Backend{
LimitRateAfter: 0,
LimitRate: 0,
}
}
func TestWithoutAnnotations(t *testing.T) {
ing := buildIngress()
_, err := NewParser().Parse(ing)
_, err := NewParser(mockBackend{}).Parse(ing)
if err != nil {
t.Error("unexpected error with ingress without annotations")
}
@ -78,7 +89,7 @@ func TestBadRateLimiting(t *testing.T) {
data[limitRPM] = "0"
ing.SetAnnotations(data)
_, err := NewParser().Parse(ing)
_, err := NewParser(mockBackend{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error with invalid limits (0)")
}
@ -87,9 +98,12 @@ func TestBadRateLimiting(t *testing.T) {
data[limitIP] = "5"
data[limitRPS] = "100"
data[limitRPM] = "10"
data[limitRATEAFTER] = "100"
data[limitRATE] = "10"
ing.SetAnnotations(data)
i, err := NewParser().Parse(ing)
i, err := NewParser(mockBackend{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -106,4 +120,10 @@ func TestBadRateLimiting(t *testing.T) {
if rateLimit.RPM.Limit != 10 {
t.Errorf("expected 10 in limit by rpm but %v was returend", rateLimit.RPM)
}
if rateLimit.LimitRateAfter != 100 {
t.Errorf("expected 100 in limit by limitrateafter but %v was returend", rateLimit.LimitRateAfter)
}
if rateLimit.LimitRate != 10 {
t.Errorf("expected 10 in limit by limitrate but %v was returend", rateLimit.LimitRate)
}
}

View file

@ -62,7 +62,7 @@ func newAnnotationExtractor(cfg extractorConfig) annotationExtractor {
"Whitelist": ipwhitelist.NewParser(cfg),
"UsePortInRedirects": portinredirect.NewParser(cfg),
"Proxy": proxy.NewParser(cfg),
"RateLimit": ratelimit.NewParser(),
"RateLimit": ratelimit.NewParser(cfg),
"Redirect": rewrite.NewParser(cfg),
"SecureUpstream": secureupstream.NewParser(cfg),
"ServiceUpstream": serviceupstream.NewParser(),

View file

@ -88,4 +88,15 @@ type Backend struct {
// WhitelistSourceRange allows limiting access to certain client addresses
// http://nginx.org/en/docs/http/ngx_http_access_module.html
WhitelistSourceRange []string `json:"whitelist-source-range,-"`
// Limits the rate of response transmission to a client.
// The rate is specified in bytes per second. The zero value disables rate limiting.
// The limit is set per a request, and so if a client simultaneously opens two connections,
// the overall rate will be twice as much as the specified limit.
// http://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate
LimitRate int `json:"limit-rate"`
// Sets the initial amount after which the further transmission of a response to a client will be rate limited.
// http://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate_after
LimitRateAfter int `json:"limit-rate-after"`
}