Skip to content

Instantly share code, notes, and snippets.

@SirPhemmiey
Last active June 6, 2024 00:54
Show Gist options
  • Save SirPhemmiey/a19af4b469d5a67787ba14f8eeccb1d4 to your computer and use it in GitHub Desktop.
Save SirPhemmiey/a19af4b469d5a67787ba14f8eeccb1d4 to your computer and use it in GitHub Desktop.
Circuit breaker with exponential backoff
package main
import (
"fmt"
"math"
"math/rand"
"net/http"
"time"
"github.com/sony/gobreaker"
)
var (
callExternalAPI func() (int, error)
)
func defaultCallExternalAPI() (int, error) {
resp, err := http.Get("https://example.com/api")
if err != nil {
return 0, err
}
defer resp.Body.Close()
return resp.StatusCode, nil
}
// exponentialBackoff returns a duration with an exponential backoff strategy
func exponentialBackoff(attempt int) time.Duration {
min := float64(time.Second)
max := float64(30 * time.Second)
backoff := min * math.Pow(2, float64(attempt))
if backoff > max {
backoff = max
}
jitter := rand.Float64() * backoff
return time.Duration(jitter)
}
func main() {
callExternalAPI = defaultCallExternalAPI
settings := gobreaker.Settings{
Name: "API Circuit Breaker",
MaxRequests: 5,
Interval: 60 * time.Second,
Timeout: 30 * time.Second,
ReadyToTrip: func(counts gobreaker.Counts) bool {
return counts.ConsecutiveFailures > 3
},
OnStateChange: func(name string, from gobreaker.State, to gobreaker.State) {
fmt.Printf("Circuit Breaker %s changed from %s to %s\n", name, from, to)
},
}
cb := gobreaker.NewCircuitBreaker(settings)
http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
// _, err := cb.Execute(func() (interface{}, error) {
// return callExternalAPI()
// })
var result interface{}
var err error
attempts := 5
for i := 0; i < attempts; i++ {
result, err = cb.Execute(func() (interface{}, error) {
return callExternalAPI()
})
if err == nil {
break
}
time.Sleep(exponentialBackoff(i))
}
if err != nil {
http.Error(w, "Service Unavailable", http.StatusServiceUnavailable)
return
}
w.Write([]byte(fmt.Sprintf("Request succeeded: %v", result)))
})
fmt.Println("Starting server on :8111...")
if err := http.ListenAndServe(":8111", nil); err != nil {
fmt.Printf("Server failed to start: %v\n", err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment