Skip to content

Commit

Permalink
Implement rate limit
Browse files Browse the repository at this point in the history
  • Loading branch information
dtomcej authored and traefiker committed Oct 30, 2019
1 parent 94d459b commit 50b9082
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 1 deletion.
14 changes: 14 additions & 0 deletions docs/content/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,17 @@ The circuit breaker protects your system from stacking requests to unhealthy ser
When your system is healthy, the circuit is closed (normal operations). When your system becomes unhealthy, the circuit opens, and requests are no longer forwarded (but handled by a fallback mechanism).
All configuration options are available [here](https://docs.traefik.io/v2.0/middlewares/circuitbreaker/#configuration-options)
### Rate Limit
Rate limiting can be enabled by using the following annotations:
```yaml
maesh.containo.us/ratelimit-average: "100"
maesh.containo.us/ratelimit-burst: "200"
```
These annotation sets average and burst requests per second limit for the service.
Please note that this value is a string, and needs to be quoted.
Further details about the rate limiting can be found [here](https://docs.traefik.io/v2.0/middlewares/ratelimit/#configuration-options)
4 changes: 4 additions & 0 deletions internal/k8s/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const (
AnnotationRetryAttempts = baseAnnotation + "retry-attempts"
// AnnotationCircuitBreakerExpression circuit breaker expression annotation.
AnnotationCircuitBreakerExpression = baseAnnotation + "circuit-breaker-expression"
// AnnotationRateLimitAverage sets the average value for rate limiting.
AnnotationRateLimitAverage = baseAnnotation + "ratelimit-average"
// AnnotationRateLimitBurst sets the burst value for rate limiting.
AnnotationRateLimitBurst = baseAnnotation + "ratelimit-burst"

// ServiceTypeHTTP HTTP service type.
ServiceTypeHTTP string = "http"
Expand Down
27 changes: 26 additions & 1 deletion internal/providers/kubernetes/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,15 @@ func (p *Provider) BuildConfig() (*dynamic.Configuration, error) {
func (p *Provider) buildHTTPMiddlewares(annotations map[string]string) *dynamic.Middleware {
circuitBreaker := buildCircuitBreakerMiddleware(annotations)
retry := buildRetryMiddleware(annotations)
rateLimit := buildRateLimitMiddleware(annotations)

if circuitBreaker == nil && retry == nil {
if circuitBreaker == nil && retry == nil && rateLimit == nil {
return nil
}

return &dynamic.Middleware{
CircuitBreaker: circuitBreaker,
RateLimit: rateLimit,
Retry: retry,
}
}
Expand Down Expand Up @@ -215,6 +217,29 @@ func buildRetryMiddleware(annotations map[string]string) *dynamic.Retry {
return nil
}

func buildRateLimitMiddleware(annotations map[string]string) *dynamic.RateLimit {
if annotations[k8s.AnnotationRateLimitAverage] != "" || annotations[k8s.AnnotationRateLimitBurst] != "" {
rlAverage, err := strconv.Atoi(annotations[k8s.AnnotationRateLimitAverage])
if err != nil {
log.Errorf("Could not parse rateLimit average annotation: %v", err)
}

rlBurst, err := strconv.Atoi(annotations[k8s.AnnotationRateLimitBurst])
if err != nil {
log.Errorf("Could not parse rateLimit burst annotation: %v", err)
}

if rlAverage > 0 && rlBurst > 1 {
return &dynamic.RateLimit{
Average: int64(rlAverage),
Burst: int64(rlBurst),
}
}
}

return nil
}

func (p *Provider) getMeshPort(serviceName, serviceNamespace string, servicePort int32) int {
for port, v := range p.tcpStateTable.Table {
if v.Name == serviceName && v.Namespace == serviceNamespace && v.Port == servicePort {
Expand Down
29 changes: 29 additions & 0 deletions internal/providers/kubernetes/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,35 @@ func TestBuildHTTPMiddlewares(t *testing.T) {
},
expected: nil,
},
{
desc: "parseable rate limit",
annotations: map[string]string{
k8s.AnnotationRateLimitAverage: "100",
k8s.AnnotationRateLimitBurst: "200",
},
expected: &dynamic.Middleware{
RateLimit: &dynamic.RateLimit{
Average: 100,
Burst: 200,
},
},
},
{
desc: "empty rate limit",
annotations: map[string]string{
k8s.AnnotationRateLimitAverage: "",
k8s.AnnotationRateLimitBurst: "",
},
expected: nil,
},
{
desc: "unparseable rate limit",
annotations: map[string]string{
k8s.AnnotationRateLimitAverage: "foo",
k8s.AnnotationRateLimitBurst: "bar",
},
expected: nil,
},
}

for _, test := range testCases {
Expand Down

0 comments on commit 50b9082

Please sign in to comment.