core/circuitbreaker/rule.go (80 lines of code) (raw):
// Copyright 1999-2020 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package circuitbreaker
import (
"fmt"
"github.com/alibaba/sentinel-golang/util"
)
// Strategy represents the strategy of circuit breaker.
// Each strategy is associated with one rule type.
type Strategy uint32
const (
// SlowRequestRatio strategy changes the circuit breaker state based on slow request ratio
SlowRequestRatio Strategy = iota
// ErrorRatio strategy changes the circuit breaker state based on error request ratio
ErrorRatio
// ErrorCount strategy changes the circuit breaker state based on error amount
ErrorCount
)
func (s Strategy) String() string {
switch s {
case SlowRequestRatio:
return "SlowRequestRatio"
case ErrorRatio:
return "ErrorRatio"
case ErrorCount:
return "ErrorCount"
default:
return "Undefined"
}
}
// Rule encompasses the fields of circuit breaking rule.
type Rule struct {
// unique id
Id string `json:"id,omitempty"`
// resource name
Resource string `json:"resource"`
Strategy Strategy `json:"strategy"`
// RetryTimeoutMs represents recovery timeout (in milliseconds) before the circuit breaker opens.
// During the open period, no requests are permitted until the timeout has elapsed.
// After that, the circuit breaker will transform to half-open state for trying a few "trial" requests.
RetryTimeoutMs uint32 `json:"retryTimeoutMs"`
// MinRequestAmount represents the minimum number of requests (in an active statistic time span)
// that can trigger circuit breaking.
MinRequestAmount uint64 `json:"minRequestAmount"`
// StatIntervalMs represents statistic time interval of the internal circuit breaker (in ms).
// Currently, the statistic interval is collected by sliding window.
StatIntervalMs uint32 `json:"statIntervalMs"`
// StatSlidingWindowBucketCount represents the bucket count of statistic sliding window.
// The statistic will be more precise as the bucket count increases, but the memory cost increases too.
// The following must be true — “StatIntervalMs % StatSlidingWindowBucketCount == 0”,
// otherwise StatSlidingWindowBucketCount will be replaced by 1.
// If it is not set, default value 1 will be used.
StatSlidingWindowBucketCount uint32 `json:"statSlidingWindowBucketCount"`
// MaxAllowedRtMs indicates that any invocation whose response time exceeds this value (in ms)
// will be recorded as a slow request.
// MaxAllowedRtMs only takes effect for SlowRequestRatio strategy
MaxAllowedRtMs uint64 `json:"maxAllowedRtMs"`
// Threshold represents the threshold of circuit breaker.
// for SlowRequestRatio, it represents the max slow request ratio
// for ErrorRatio, it represents the max error request ratio
// for ErrorCount, it represents the max error request count
Threshold float64 `json:"threshold"`
// ProbeNum is number of probes required when the circuit breaker is half-open.
// when the probe num are set and circuit breaker in the half-open state.
// if err occurs during the probe, the circuit breaker is opened immediately.
// otherwise,the circuit breaker is closed only after the number of probes is reached
ProbeNum uint64 `json:"probeNum"`
}
func (r *Rule) String() string {
// fallback string
return fmt.Sprintf("{id=%s, resource=%s, strategy=%s, RetryTimeoutMs=%d, MinRequestAmount=%d, StatIntervalMs=%d, StatSlidingWindowBucketCount=%d, MaxAllowedRtMs=%d, Threshold=%f}",
r.Id, r.Resource, r.Strategy, r.RetryTimeoutMs, r.MinRequestAmount, r.StatIntervalMs, r.StatSlidingWindowBucketCount, r.MaxAllowedRtMs, r.Threshold)
}
func (r *Rule) isStatReusable(newRule *Rule) bool {
if newRule == nil {
return false
}
return r.Resource == newRule.Resource && r.Strategy == newRule.Strategy && r.StatIntervalMs == newRule.StatIntervalMs &&
r.StatSlidingWindowBucketCount == newRule.StatSlidingWindowBucketCount
}
func (r *Rule) ResourceName() string {
return r.Resource
}
// Check whether the fields shared by all rule strategy types are consistent
func (r *Rule) isEqualsToBase(newRule *Rule) bool {
if newRule == nil {
return false
}
return r.Resource == newRule.Resource && r.Strategy == newRule.Strategy && r.RetryTimeoutMs == newRule.RetryTimeoutMs &&
r.MinRequestAmount == newRule.MinRequestAmount && r.StatIntervalMs == newRule.StatIntervalMs && r.StatSlidingWindowBucketCount == newRule.StatSlidingWindowBucketCount &&
r.ProbeNum == newRule.ProbeNum
}
func (r *Rule) isEqualsTo(newRule *Rule) bool {
if !r.isEqualsToBase(newRule) {
return false
}
switch newRule.Strategy {
case SlowRequestRatio:
return r.MaxAllowedRtMs == newRule.MaxAllowedRtMs && util.Float64Equals(r.Threshold, newRule.Threshold)
case ErrorRatio:
return util.Float64Equals(r.Threshold, newRule.Threshold)
case ErrorCount:
return util.Float64Equals(r.Threshold, newRule.Threshold)
default:
return false
}
}
func getRuleStatSlidingWindowBucketCount(r *Rule) uint32 {
interval := r.StatIntervalMs
bucketCount := r.StatSlidingWindowBucketCount
if bucketCount == 0 || interval%bucketCount != 0 {
bucketCount = 1
}
return bucketCount
}