github.com/cloudwego/kitex@v0.9.0/pkg/circuitbreak/circuitbreak.go (about)

     1  /*
     2   * Copyright 2021 CloudWeGo Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package circuitbreak
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  
    23  	"github.com/bytedance/gopkg/cloud/circuitbreaker"
    24  
    25  	"github.com/cloudwego/kitex/pkg/endpoint"
    26  	"github.com/cloudwego/kitex/pkg/kerrors"
    27  )
    28  
    29  // Parameter contains parameters for circuit breaker.
    30  type Parameter struct {
    31  	// Enabled means if to enable the circuit breaker.
    32  	Enabled bool
    33  	// ErrorRate means the rate at which breaks.
    34  	ErrorRate float64
    35  	// MinimalSample means the minimal sample need before break.
    36  	MinimalSample int64
    37  }
    38  
    39  // ErrorType means the error type.
    40  type ErrorType int
    41  
    42  // Constants for ErrorType.
    43  const (
    44  	// TypeIgnorable means ignorable error, which is ignored by the circuit breaker.
    45  	TypeIgnorable ErrorType = iota
    46  	// TypeTimeout means timeout error.
    47  	TypeTimeout
    48  	// TypeFailure means the request failed, but it isn't timeout.
    49  	TypeFailure
    50  	// TypeSuccess means the request successes.
    51  	TypeSuccess
    52  )
    53  
    54  // WrapErrorWithType is used to define the ErrorType for CircuitBreaker.
    55  // If you don't want the error trigger fuse, you can set the ErrorType to TypeIgnorable,
    56  // the error won't be regarded as failed.
    57  // eg: return circuitbreak.WrapErrorWithType.WithCause(err, circuitbreak.TypeIgnorable) in customized middleware.
    58  func WrapErrorWithType(err error, errorType ErrorType) CircuitBreakerAwareError {
    59  	return &errorWrapperWithType{err: err, errType: errorType}
    60  }
    61  
    62  // Control is the control strategy of the circuit breaker.
    63  type Control struct {
    64  	// Implement this to generate a key for the circuit breaker panel.
    65  	GetKey func(ctx context.Context, request interface{}) (key string, enabled bool)
    66  
    67  	// Implement this to determine the type of error.
    68  	GetErrorType func(ctx context.Context, request, response interface{}, err error) ErrorType
    69  
    70  	// Implement this to provide more detailed information about the circuit breaker.
    71  	// The err argument is always a kerrors.ErrCircuitBreak.
    72  	DecorateError func(ctx context.Context, request interface{}, err error) error
    73  }
    74  
    75  // NewCircuitBreakerMW creates a circuit breaker MW using the given Control strategy and Panel.
    76  func NewCircuitBreakerMW(control Control, panel circuitbreaker.Panel) endpoint.Middleware {
    77  	return func(next endpoint.Endpoint) endpoint.Endpoint {
    78  		return func(ctx context.Context, request, response interface{}) (err error) {
    79  			key, enabled := control.GetKey(ctx, request)
    80  			if !enabled {
    81  				return next(ctx, request, response)
    82  			}
    83  
    84  			if !panel.IsAllowed(key) {
    85  				return control.DecorateError(ctx, request, kerrors.ErrCircuitBreak)
    86  			}
    87  
    88  			err = next(ctx, request, response)
    89  			RecordStat(ctx, request, response, err, key, &control, panel)
    90  			return
    91  		}
    92  	}
    93  }
    94  
    95  // RecordStat to report request result to circuit breaker
    96  func RecordStat(ctx context.Context, request, response interface{}, err error, cbKey string, ctl *Control, panel circuitbreaker.Panel) {
    97  	switch ctl.GetErrorType(ctx, request, response, err) {
    98  	case TypeTimeout:
    99  		panel.Timeout(cbKey)
   100  	case TypeFailure:
   101  		panel.Fail(cbKey)
   102  	case TypeSuccess:
   103  		panel.Succeed(cbKey)
   104  	}
   105  }
   106  
   107  // CircuitBreakerAwareError is used to wrap ErrorType
   108  type CircuitBreakerAwareError interface {
   109  	error
   110  	TypeForCircuitBreaker() ErrorType
   111  }
   112  
   113  type errorWrapperWithType struct {
   114  	errType ErrorType
   115  	err     error
   116  }
   117  
   118  func (e errorWrapperWithType) TypeForCircuitBreaker() ErrorType {
   119  	return e.errType
   120  }
   121  
   122  func (e errorWrapperWithType) Error() string {
   123  	return e.err.Error()
   124  }
   125  
   126  func (e errorWrapperWithType) Unwrap() error {
   127  	return e.err
   128  }
   129  
   130  func (e errorWrapperWithType) Is(target error) bool {
   131  	return errors.Is(e.err, target)
   132  }