Documentation
¶
Overview ¶
Package cutout implements the circuit breaker design pattern(see: https://martinfowler.com/bliki/CircuitBreaker.html) for calling third party api services.
Cutout comes with features like:
1. Multilevel fallback functions(in case even the fallback fails)
2. Custom BackOff function on the request level for generating backoff timeout logics
3. Event channel to capture events like State change or failure detection
Here is a basic example:
package main
import (
"bytes"
"fmt"
"log"
"net/http"
"time"
"github.com/Anondo/cutout"
)
var (
cb = &cutout.CircuitBreaker{
FailThreshold: 100,
HealthCheckPeriod: 15 * time.Second,
}
req = cutout.Request{
URL: "http://localhost:9090",
AllowedStatus: []int{http.StatusOK},
Method: http.MethodPost,
TimeOut: 2 * time.Second,
RequestBody: bytes.NewBuffer([]byte(`{"name":"abcd"}`)),
BackOff: func(t time.Duration) time.Duration {
return time.Duration(int(t/time.Second)*5) * time.Second
},
}
cache = `{"name":"Mr. Test","age":69,"cgpa":4}`
)
func thehandler(w http.ResponseWriter, r *http.Request) {
resp, err := cb.Call(&req, func() (*cutout.Response, error) {
return &cutout.Response{
BodyString: cache,
}, nil
})
if err != nil {
fmt.Fprintf(w, err.Error())
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, resp.BodyString)
}
func main() {
http.HandleFunc("/", thehandler)
log.Println("Service A is running on http://localhost:8080 ...")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err.Error())
}
}
Index ¶
- Constants
- type Analytics
- type CircuitBreaker
- func (c *CircuitBreaker) Call(req *Request, fallbackFuncs ...func() (*Response, error)) (*Response, error)
- func (c *CircuitBreaker) CallWithCustomRequest(req *http.Request, allowedStatus []int, ...) (*Response, error)
- func (c *CircuitBreaker) FailCount() int
- func (c *CircuitBreaker) GetAnalytics() *Analytics
- func (c *CircuitBreaker) InitAnalytics()
- func (c *CircuitBreaker) InitEvent(e chan string)
- func (c *CircuitBreaker) LastFailed() *time.Time
- func (c *CircuitBreaker) State() string
- type Failure
- type Request
- type RequestRecord
- type Response
Constants ¶
const ( StateChangeEvent = "STATE_CHANGE" FailureEvent = "FAILURE" )
Events
const ( ClosedState = "CLOSED" OpenState = "OPEN" HalfOpenState = "HALF_OPEN" )
states of the circuit breaker
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Analytics ¶
type Analytics struct {
RequestSent int `json:"request_sent"`
TotalFailures int `json:"total_failures"`
FallbackCalls int `json:"fallback_calls"`
Failures []Failure `json:"failures"`
TotalCalls int `json:"total_calls"`
SuccessRate float64 `json:"success_rate"`
FailureRate float64 `json:"failure_rate"`
RequestRecords []RequestRecord `json:"request_records"`
}
Analytics contains analytical informations regarding the circuit breaker
type CircuitBreaker ¶
type CircuitBreaker struct {
FailThreshold int
HealthCheckPeriod time.Duration
// contains filtered or unexported fields
}
CircuitBreaker is the circuit breaker!!!
func NewCircuitBreaker ¶
func NewCircuitBreaker(failThreshold int, healthCheckPeriod time.Duration) *CircuitBreaker
NewCircuitBreaker creates a new circuit breaker
func (*CircuitBreaker) Call ¶
func (c *CircuitBreaker) Call(req *Request, fallbackFuncs ...func() (*Response, error)) (*Response, error)
Call calls an external service using the circuit breaker design
Parameters:
1. *cutout.Request -------> The request object
2. ...func()(*Response , error) -----> one or many fallback functions which must return a *cutout.Response & error instance
Example:
resp, err := cb.Call(&req, func() (*cutout.Response, error) {
return &cutout.Response{
BodyString: cache,
}, nil
})
func (*CircuitBreaker) CallWithCustomRequest ¶
func (c *CircuitBreaker) CallWithCustomRequest(req *http.Request, allowedStatus []int, fallbackFuncs ...func() (*Response, error)) (*Response, error)
CallWithCustomRequest calls an external service using the circuit breaker design with a custom request function
Parameters:
1. *http.Request -------> The request object of the built-in http package
2. []int -------> Allowed http status codes, which wont be counted as failures
3. ...func()(*Response , error) -----> one or many fallback functions which must return a *cutout.Response & error instance
Example:
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
req = req.WithContext(ctx)
resp, err := cb.CallWithCustomRequest(req, []int{http.StatusOK}, func() (*Response, error) {
return &Response{
BodyString: cache,
Response: &http.Response{
StatusCode: http.StatusOK,
},
}, nil
})
func (*CircuitBreaker) FailCount ¶
func (c *CircuitBreaker) FailCount() int
FailCount returns the count of failure
func (*CircuitBreaker) GetAnalytics ¶
func (c *CircuitBreaker) GetAnalytics() *Analytics
GetAnalytics returns the analytics instance of the circuit breaker
func (*CircuitBreaker) InitAnalytics ¶
func (c *CircuitBreaker) InitAnalytics()
InitAnalytics initializes the analytics instance for the circu breaker to start analyzing
func (*CircuitBreaker) InitEvent ¶
func (c *CircuitBreaker) InitEvent(e chan string)
InitEvent initializes the circuit breaker events NOTE: the parameter must be a buffered channel
Parameters:
1. chan string ------> the event channel, must be a buffered channel so that the circuit doesn't get blocked out
Example:
events := make(chan string, 2)
cb.InitEvent(events)
go func() {
log.Println("Waiting for events from circuit breaker...")
for {
switch <-events {
case cutout.StateChangeEvent:
log.Println("Status change occured")
log.Println("Current state:", cb.State())
case cutout.FailureEvent:
log.Println("Failure occured")
}
}
log.Println("Done with the waiting")
}()
func (*CircuitBreaker) LastFailed ¶
func (c *CircuitBreaker) LastFailed() *time.Time
LastFailed returns the time object of the last failure
func (*CircuitBreaker) State ¶
func (c *CircuitBreaker) State() string
State returns the current satte of the circuit
type Failure ¶
type Failure struct {
Message string `json:"message"`
OccurredAt time.Time `json:"occurred_at"`
TotalFailures int `json:"total_failures"`
}
Failure holds the failure instance information
type Request ¶
type Request struct {
URL string
Method string
RequestBody *bytes.Buffer
Headers map[string]string
AllowedStatus []int
TimeOut time.Duration
BackOff func(time.Duration) time.Duration
}
Request represents the data needed to make http requests
type RequestRecord ¶
type RequestRecord struct {
Name string `json:"name"`
Method string `json:"method"`
StatusCode int `json:"status_code"`
StatusText string `json:"status_text"`
Message string `json:"message"`
RequestedAt time.Time `json:"requested_at"`
}
RequestRecord holds the information of a request incident

