Skip to content

Commit

Permalink
update circuitbreaker (easegress-io#688)
Browse files Browse the repository at this point in the history
* update circuitbreaker

* update circuitbreaker

* update circuitbreaker and timelimiter

* update circuitbreaker

* update test
  • Loading branch information
suchen-sci committed Jul 7, 2022
1 parent 2294c49 commit 84fba19
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 82 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ The architecture of Easegress:
- MQTT
- **Rich Routing Rules:** exact path, path prefix, regular expression of the path, method, headers.
- **Resilience&Fault Tolerance**
- **Circuit break:** temporarily blocks possible failures.
- **Rate limit:** limits the rate of incoming requests.
- **CircuitBreaker:** temporarily blocks possible failures.
- **RateLimiter:** limits the rate of incoming requests.
- **Retry:** repeats failed executions.
- **Time limit:** limits the duration of execution.
- **TimeLimiter:** limits the duration of execution.
- **Deployment Management**
- **Blue-green Strategy:** switches traffic at one time.
- **Canary Strategy:** schedules traffic slightly.
Expand Down Expand Up @@ -121,7 +121,7 @@ The following examples show how to use Easegress for different scenarios.
- [MQTTProxy](./doc/cookbook/mqtt-proxy.md) - An Example to MQTT proxy with Kafka backend.
- [Performance](./doc/cookbook/performance.md) - Performance optimization - compression, caching etc.
- [Pipeline](./doc/cookbook/pipeline.md) - How to orchestrate HTTP filters for requests/responses handling
- [Resilience and Fault Tolerance](./doc/cookbook/resilience.md) - Circuit Break, Rate Limit, Retry, Time Limit, etc. (Porting from [Java resilience4j](https://github.com/resilience4j/resilience4j))
- [Resilience and Fault Tolerance](./doc/cookbook/resilience.md) - CircuitBreaker, RateLimiter, Retry, TimeLimiter, etc. (Porting from [Java resilience4j](https://github.com/resilience4j/resilience4j))
- [Security](./doc/cookbook/security.md) - How to do authentication by Header, JWT, HMAC, OAuth2, etc.
- [Service Proxy](./doc/cookbook/service-proxy.md) - Supporting the Microservice registries - Zookeeper, Eureka, Consul, Nacos, etc.
- [WebAssembly](./doc/cookbook/wasm.md) - Using AssemblyScript to extend the Easegress
Expand Down
2 changes: 1 addition & 1 deletion doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This is a cookbook that lists a number of useful and practical examples on how t
- [Migrate v1.x Filter To v2.x](./cookbook/migrate-v1-filter-to-v2.md) - How to migrate a v1.x filter to v2.x.
- [Performance](./cookbook/performance.md) - Performance optimization - compression, caching etc.
- [Pipeline](./cookbook/pipeline.md) - How to orchestrate HTTP filters for requests/responses handling
- [Resilience and Fault Tolerance](./cookbook/resilience.md) - Circuit Breaker, Rate Limiter, Retryer, Time limiter, etc. (Porting from [Java resilience4j](https://github.com/resilience4j/resilience4j))
- [Resilience and Fault Tolerance](./cookbook/resilience.md) - CircuitBreaker, RateLimiter, Retry, TimeLimiter, etc. (Porting from [Java resilience4j](https://github.com/resilience4j/resilience4j))
- [Security](./cookbook/security.md) - How to do authentication by Header, JWT, HMAC, OAuth2, etc.
- [Service Proxy](./cookbook/service-proxy.md) - Supporting the Microservice registries - Zookeeper, Eureka, Consul, Nacos, etc.
- [WebAssembly](./cookbook/wasm.md) - Using AssemblyScript to extend the Easegress
Expand Down
40 changes: 20 additions & 20 deletions doc/cookbook/resilience.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
- [Resilience](#resilience)
- [Basic: Load Balance](#basic-load-balance)
- [More Livingness: Resilience of Service](#more-livingness-resilience-of-service)
- [Circuit Break](#circuit-break)
- [CircuitBreaker](#circuitbreaker)
- [RateLimiter](#ratelimiter)
- [Retry](#retry)
- [Time Limit](#time-limit)
- [TimeLimiter](#timelimiter)
- [References](#references)
- [Circuit Break](#circuit-break-1)
- [CircuitBreaker](#circuitbreaker-1)
- [RateLimiter](#ratelimiter-1)
- [Retry](#retry-1)
- [Concepts](#concepts)
Expand Down Expand Up @@ -42,9 +42,9 @@ filters:

## More Livingness: Resilience of Service

### Circuit Break
### CircuitBreaker

Circuit Break leverges a finite state machine to implement the processing
CircuitBreaker leverges a finite state machine to implement the processing
logic, the state machine has three states: `CLOSED`, `OPEN`, and `HALF_OPEN`.
When the state is `CLOSED`, requests pass through normally, state transits
to `OPEN` if request failure rate or slow request rate reach a configured
Expand Down Expand Up @@ -81,12 +81,12 @@ filters:
- url: http:https://127.0.0.1:9097
loadBalance:
policy: roundRobin
circuitBreakPolicy: countBased
circuitBreakerPolicy: countBased
failureCodes: [500, 503, 504]

resilience:
- name: countBased
kind: CircuitBreak
kind: CircuitBreaker
slidingWindowType: COUNT_BASED
failureRateThreshold: 50
slidingWindowSize: 100
Expand All @@ -98,7 +98,7 @@ if more than 60% of the requests within the last 200 seconds failed.
```yaml
resilience:
- name: time-based-policy
kind: CircuitBreak
kind: CircuitBreaker
slidingWindowType: TIME_BASED
failureRateThreshold: 60
slidingWindowSize: 200
Expand All @@ -111,7 +111,7 @@ requests and short-circuits requests if 60% of recent requests are slow.
```yaml
resilience:
- name: countBased
kind: CircuitBreak
kind: CircuitBreaker
slowCallRateThreshold: 60
slowCallDurationThreshold: 30s
```
Expand All @@ -123,7 +123,7 @@ cases, we can avoid it by specifying `minimumNumberOfCalls`.
```yaml
resilience:
- name: countBased
kind: CircuitBreak
kind: CircuitBreaker
minimumNumberOfCalls: 10
```

Expand All @@ -133,7 +133,7 @@ wait duration in the `half-open` state:
```yaml
resilience:
- name: countBased
kind: CircuitBreak
kind: CircuitBreaker
waitDurationInOpenState: 2m
maxWaitDurationInHalfOpenState: 1m
```
Expand All @@ -143,12 +143,12 @@ In the `half-open` state, we can limit the number of permitted requests:
```yaml
resilience:
- name: countBased
kind: CircuitBreak
kind: CircuitBreaker
permittedNumberOfCallsInHalfOpenState: 10
```

For the full YAML, see [here](#circuit-break-1), and please refer
[Circuit Break Policy](../reference/controllers.md#circuit-break-policy]
For the full YAML, see [here](#circuitbreaker-1), and please refer
[CircuitBreaker Policy](../reference/controllers.md#circuitbreaker-policy]
for more information.

### RateLimiter
Expand Down Expand Up @@ -237,9 +237,9 @@ resilience:
For the full YAML, see [here](#retry-1), and please refer
[Retry Policy](../reference/controllers.md#retry-policy] for more information.

### Time Limit
### TimeLimiter

Time limit limits the time of requests, a request is canceled if it cannot
TimeLimiter limits the time of requests, a request is canceled if it cannot
get a response in configured duration. As this resilience type only requires
config a timeout duration, it is implemented directly on filters like `Proxy`.

Expand All @@ -266,7 +266,7 @@ filters:

## References

### Circuit Break
### CircuitBreaker

```yaml
name: pipeline-reverse-proxy
Expand All @@ -285,12 +285,12 @@ filters:
- url: http:https://127.0.0.1:9097
loadBalance:
policy: roundRobin
circuitBreakPolicy: countBasedPolicy
circuitBreakerPolicy: countBasedPolicy
failureCodes: [500, 503, 504]

resilience:
- name: countBasedPolicy
kind: CircuitBreak
kind: CircuitBreaker
slidingWindowType: COUNT_BASED
failureRateThreshold: 50
slidingWindowSize: 100
Expand All @@ -301,7 +301,7 @@ resilience:
maxWaitDurationInHalfOpenState: 1m
permittedNumberOfCallsInHalfOpenState: 10
- name: timeBasedPolicy
kind: CircuitBreak
kind: CircuitBreaker
slidingWindowType: TIME_BASED
failureRateThreshold: 60
slidingWindowSize: 200
Expand Down
16 changes: 8 additions & 8 deletions doc/reference/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
- [autocertmanager.DomainSpec](#autocertmanagerdomainspec)
- [resilience.Policy](#resiliencepolicy)
- [Retry Policy](#retry-policy)
- [Circuit Break Policy](#circuit-break-policy)
- [CircuitBreaker Policy](#circuitbreaker-policy)

As the [architecture diagram](../imgs/architecture.png) shows, the controller is the core entity to control kinds of working. There are two kinds of controllers overall:

Expand Down Expand Up @@ -158,7 +158,7 @@ In this case, if a request’s header doesn’t have the key `X-Id` or its value

The `resilience` field defines resilience policies, if a filter implements the `filters.Resiliencer` interface (for now, only the `Proxy` filter implements the interface), the pipeline injects the policies into the filter instance after creating it.
A filter can implement the `filters.Resiliencer` interface to support resilience. There are two kinds of resilience, `Retry` and `CircuitBreak`. Check [resilience](#resilience) for more details. The following config adds a retry policy to the proxy filter:
A filter can implement the `filters.Resiliencer` interface to support resilience. There are two kinds of resilience, `Retry` and `CircuitBreaker`. Check [resilience](#resilience) for more details. The following config adds a retry policy to the proxy filter:
```yaml
name: http-pipeline-example3
kind: Pipeline
Expand Down Expand Up @@ -596,9 +596,9 @@ A retry policy configures how to retry a failed request.
| backOffPolicy | string | The back-off policy for wait duration, could be `EXPONENTIAL` or `RANDOM` and the default is `RANDOM`. If configured as `EXPONENTIAL`, the base wait duration becomes 1.5 times larger after each failed attempt | No |
| randomizationFactor | float64 | Randomization factor for actual wait duration, a number in interval `[0, 1]`, default is 0. The actual wait duration used is a random number in interval `[(base wait duration) * (1 - randomizationFactor), (base wait duration) * (1 + randomizationFactor)]` | No |

#### Circuit Break Policy
#### CircuitBreaker Policy

Circuit Break leverges a finite state machine to implement the processing logic, the state machine has three states: `CLOSED`, `OPEN`, and `HALF_OPEN`. When the state is `CLOSED`, requests pass through normally, state transits to `OPEN` if request failure rate or slow request rate reach a configured threshold and requests will be shor-circuited in this state. After a configured duration, state transits from `OPEN` to `HALF_OPEN`, in which a limited number of requests are permitted to pass through while other requests are still short-circuited, and state transit to `CLOSED` or `OPEN`
CircuitBreaker leverges a finite state machine to implement the processing logic, the state machine has three states: `CLOSED`, `OPEN`, and `HALF_OPEN`. When the state is `CLOSED`, requests pass through normally, state transits to `OPEN` if request failure rate or slow request rate reach a configured threshold and requests will be shor-circuited in this state. After a configured duration, state transits from `OPEN` to `HALF_OPEN`, in which a limited number of requests are permitted to pass through while other requests are still short-circuited, and state transit to `CLOSED` or `OPEN`
based on the results of the permitted requests.

When `CLOSED`, it uses a sliding window to store and aggregate the result of recent requests, the window can either be `COUNT_BASED` or `TIME_BASED`. The `COUNT_BASED` window aggregates the last N requests and the `TIME_BASED` window aggregates requests in the last N seconds, where N is the window size.
Expand All @@ -612,14 +612,14 @@ Policy `circuit-breaker-example-time` short-circuits requests if more than 60% o
> failed means that backend filter returns non-empty results.
```yaml
kind: CircuitBreak
kind: CircuitBreaker
name: circuit-breaker-example-count
slidingWindowType: COUNT_BASED
failureRateThreshold: 50
slidingWindowSize: 100

---
kind: CircuitBreak
kind: CircuitBreaker
name: circuit-breaker-example-time
slidingWindowType: TIME_BASED
failureRateThreshold: 60
Expand All @@ -636,7 +636,7 @@ slidingWindowSize: 100
| slidingWindowSize | uint32 | The size of the sliding window which is used to record the outcome of requests when the CircuitBreaker is `CLOSED`. Default is 100 | No |
| permittedNumberOfCallsInHalfOpenState | uint32 | The number of permitted requests when the CircuitBreaker is `HALF_OPEN`. Default is 10 | No |
| minimumNumberOfCalls | uint32 | The minimum number of requests which are required (per sliding window period) before the CircuitBreaker can calculate the error rate or slow requests rate. For example, if `minimumNumberOfCalls` is 10, then at least 10 requests must be recorded before the failure rate can be calculated. If only 9 requests have been recorded the CircuitBreaker will not transition to `OPEN` even if all 9 requests have failed. Default is 10 | No |
| maxWaitDurationInHalfOpenState | string | The maximum wait duration which controls the longest amount of time a CircuitBreaker could stay in `HALF_OPEN` state before it switches to `OPEN`. Value 0 means Circuit Breaker would wait infinitely in `HALF_OPEN` State until all permitted requests have been completed. Default is 0| No |
| maxWaitDurationInHalfOpenState | string | The maximum wait duration which controls the longest amount of time a CircuitBreaker could stay in `HALF_OPEN` state before it switches to `OPEN`. Value 0 means CircuitBreaker would wait infinitely in `HALF_OPEN` State until all permitted requests have been completed. Default is 0| No |
| waitDurationInOpenState | string | The time that the CircuitBreaker should wait before transitioning from `OPEN` to `HALF_OPEN`. Default is 60s | No |

See more details about `Retry`, `CircuitBreak` or other resilience polcies in [here](../cookbook/resilience.md).
See more details about `Retry`, `CircuitBreaker` or other resilience polcies in [here](../cookbook/resilience.md).
2 changes: 1 addition & 1 deletion doc/reference/filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,7 @@ Rules to revise request header.
| serverMaxBodySize | int64 | Max size of response body, will use the option of the Proxy if not set. Responses with a body larger than this option are discarded. When this option is set to `-1`, Easegress takes the response body as a stream and the body can be any size, but some features are not possible in this case, please refer [Stream](./stream.md) for more information. | No |
| timeout | string | Request calceled when timeout | No |
| retryPolicy | string | Retry policy name | No |
| circuitBreakPolicy | string | Circuit break policy name | No |
| circuitBreakerPolicy | string | CircuitBreaker policy name | No |
| failureCodes | []int | Proxy return result of failureCode when backend resposne's status code in failureCodes | No |


Expand Down
52 changes: 26 additions & 26 deletions pkg/filters/proxy/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,31 +170,31 @@ type ServerPool struct {
name string
failureCodes map[int]struct{}

filter RequestMatcher
loadBalancer atomic.Value
timeout time.Duration
retryWrapper resilience.Wrapper
circuitbreakWrapper resilience.Wrapper
filter RequestMatcher
loadBalancer atomic.Value
timeout time.Duration
retryWrapper resilience.Wrapper
circuitBreakerWrapper resilience.Wrapper

httpStat *httpstat.HTTPStat
memoryCache *MemoryCache
}

// ServerPoolSpec is the spec for a server pool.
type ServerPoolSpec struct {
SpanName string `yaml:"spanName" jsonschema:"omitempty"`
Filter *RequestMatcherSpec `yaml:"filter" jsonschema:"omitempty"`
ServerMaxBodySize int64 `yaml:"serverMaxBodySize" jsonschema:"omitempty"`
ServerTags []string `yaml:"serverTags" jsonschema:"omitempty,uniqueItems=true"`
Servers []*Server `yaml:"servers" jsonschema:"omitempty"`
ServiceRegistry string `yaml:"serviceRegistry" jsonschema:"omitempty"`
ServiceName string `yaml:"serviceName" jsonschema:"omitempty"`
LoadBalance *LoadBalanceSpec `yaml:"loadBalance" jsonschema:"omitempty"`
Timeout string `yaml:"timeout" jsonschema:"omitempty,format=duration"`
RetryPolicy string `yaml:"retryPolicy" jsonschema:"omitempty"`
CircuitBreakPolicy string `yaml:"circuitBreakPolicy" jsonschema:"omitempty"`
FailureCodes []int `yaml:"failureCodes" jsonschema:"omitempty"`
MemoryCache *MemoryCacheSpec `yaml:"memoryCache,omitempty" jsonschema:"omitempty"`
SpanName string `yaml:"spanName" jsonschema:"omitempty"`
Filter *RequestMatcherSpec `yaml:"filter" jsonschema:"omitempty"`
ServerMaxBodySize int64 `yaml:"serverMaxBodySize" jsonschema:"omitempty"`
ServerTags []string `yaml:"serverTags" jsonschema:"omitempty,uniqueItems=true"`
Servers []*Server `yaml:"servers" jsonschema:"omitempty"`
ServiceRegistry string `yaml:"serviceRegistry" jsonschema:"omitempty"`
ServiceName string `yaml:"serviceName" jsonschema:"omitempty"`
LoadBalance *LoadBalanceSpec `yaml:"loadBalance" jsonschema:"omitempty"`
Timeout string `yaml:"timeout" jsonschema:"omitempty,format=duration"`
RetryPolicy string `yaml:"retryPolicy" jsonschema:"omitempty"`
CircuitBreakerPolicy string `yaml:"circuitBreakerPolicy" jsonschema:"omitempty"`
FailureCodes []int `yaml:"failureCodes" jsonschema:"omitempty"`
MemoryCache *MemoryCacheSpec `yaml:"memoryCache,omitempty" jsonschema:"omitempty"`
}

// ServerPoolStatus is the status of Pool.
Expand Down Expand Up @@ -351,17 +351,17 @@ func (sp *ServerPool) InjectResiliencePolicy(policies map[string]resilience.Poli
sp.retryWrapper = policy.CreateWrapper()
}

name = sp.spec.CircuitBreakPolicy
name = sp.spec.CircuitBreakerPolicy
if name != "" {
p := policies[name]
if p == nil {
panic(fmt.Errorf("circuit break policy %s not found", name))
panic(fmt.Errorf("circuitbreaker policy %s not found", name))
}
policy, ok := p.(*resilience.CircuitBreakPolicy)
policy, ok := p.(*resilience.CircuitBreakerPolicy)
if !ok {
panic(fmt.Errorf("policy %s is not a circuit break policy", name))
panic(fmt.Errorf("policy %s is not a circuitBreaker policy", name))
}
sp.circuitbreakWrapper = policy.CreateWrapper()
sp.circuitBreakerWrapper = policy.CreateWrapper()
}
}

Expand Down Expand Up @@ -475,8 +475,8 @@ func (sp *ServerPool) handle(ctx *context.Context, mirror bool) string {
if sp.retryWrapper != nil && !spCtx.req.IsStream() {
handler = sp.retryWrapper.Wrap(handler)
}
if sp.circuitbreakWrapper != nil {
handler = sp.circuitbreakWrapper.Wrap(handler)
if sp.circuitBreakerWrapper != nil {
handler = sp.circuitBreakerWrapper.Wrap(handler)
}

// call the handler.
Expand All @@ -485,7 +485,7 @@ func (sp *ServerPool) handle(ctx *context.Context, mirror bool) string {
return ""
}

// Circuit breaker is the most outside resiliencer, if the error
// CircuitBreaker is the most outside resiliencer, if the error
// is ErrShortCircuited, we are sure the response is nil.
if err == resilience.ErrShortCircuited {
logger.Debugf("%s: short circuited by circuit break policy", sp.name)
Expand Down
10 changes: 5 additions & 5 deletions pkg/filters/proxy/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func TestInjectResilience(t *testing.T) {

yamlSpec := `spanName: test
retryPolicy: retry
circuitBreakPolicy: circuitBreak
circuitBreakerPolicy: circuitBreaker
servers:
- url: http:https://192.168.1.1
`
Expand All @@ -94,20 +94,20 @@ servers:

assert.Panics(func() { sp.InjectResiliencePolicy(policies) })

policies["retry"] = &resilience.CircuitBreakPolicy{}
policies["retry"] = &resilience.CircuitBreakerPolicy{}
assert.Panics(func() { sp.InjectResiliencePolicy(policies) })

policies["retry"] = &resilience.RetryPolicy{}
assert.Panics(func() { sp.InjectResiliencePolicy(policies) })

policies["circuitBreak"] = &resilience.RetryPolicy{}
policies["circuitBreaker"] = &resilience.RetryPolicy{}
assert.Panics(func() { sp.InjectResiliencePolicy(policies) })

policies["circuitBreak"] = &resilience.CircuitBreakPolicy{}
policies["circuitBreaker"] = &resilience.CircuitBreakerPolicy{}
assert.NotPanics(func() { sp.InjectResiliencePolicy(policies) })

assert.NotNil(sp.retryWrapper)
assert.NotNil(sp.circuitbreakWrapper)
assert.NotNil(sp.circuitBreakerWrapper)
}

func TestBuildResponseFromCache(t *testing.T) {
Expand Down
Loading

0 comments on commit 84fba19

Please sign in to comment.