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 }