mirror of
https://github.com/foomo/keel.git
synced 2025-10-16 12:35:34 +00:00
Merge pull request #132 from foomo/feature/add-retry-rountripware
feat: retry roundtripware
This commit is contained in:
commit
0a22dc4913
@ -22,6 +22,7 @@ require (
|
||||
cloud.google.com/go/firestore v1.9.0 // indirect
|
||||
cloud.google.com/go/longrunning v0.3.0 // indirect
|
||||
github.com/armon/go-metrics v0.4.0 // indirect
|
||||
github.com/avast/retry-go v3.0.0+incompatible // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
|
||||
@ -62,6 +62,8 @@ github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8
|
||||
github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
|
||||
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
|
||||
27
example/roundtripwares/retry/client.go
Normal file
27
example/roundtripwares/retry/client.go
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
keellog "github.com/foomo/keel/log"
|
||||
keelhttp "github.com/foomo/keel/net/http"
|
||||
"github.com/foomo/keel/net/http/roundtripware"
|
||||
)
|
||||
|
||||
func client() {
|
||||
l := keellog.Logger()
|
||||
|
||||
client := keelhttp.NewHTTPClient(
|
||||
keelhttp.HTTPClientWithRoundTripware(l,
|
||||
roundtripware.Retry(
|
||||
roundtripware.RetryWithAttempts(12),
|
||||
roundtripware.RetryWithMaxDelay(time.Second),
|
||||
),
|
||||
))
|
||||
|
||||
_, err := client.Get("http://localhost:8080/404") //nolint:all
|
||||
keellog.Must(l, err, "failed to retrieve response")
|
||||
|
||||
fmt.Printf("Repetition process is finished with: %v\n", err) //nolint:forbidigo
|
||||
}
|
||||
13
example/roundtripwares/retry/main.go
Normal file
13
example/roundtripwares/retry/main.go
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
go server()
|
||||
|
||||
go client()
|
||||
|
||||
time.Sleep(time.Second * 60)
|
||||
}
|
||||
34
example/roundtripwares/retry/server.go
Normal file
34
example/roundtripwares/retry/server.go
Normal file
@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/foomo/keel"
|
||||
)
|
||||
|
||||
func server() {
|
||||
svr := keel.NewServer()
|
||||
|
||||
// get logger
|
||||
l := svr.Logger()
|
||||
|
||||
// create demo service
|
||||
svs := http.NewServeMux()
|
||||
|
||||
counter := 0
|
||||
svs.HandleFunc("/404", func(w http.ResponseWriter, r *http.Request) {
|
||||
counter++
|
||||
if counter < 10 {
|
||||
http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
})
|
||||
|
||||
svr.AddService(
|
||||
keel.NewServiceHTTP(l, "demo", "localhost:8080", svs),
|
||||
)
|
||||
|
||||
svr.Run()
|
||||
}
|
||||
1
go.mod
1
go.mod
@ -3,6 +3,7 @@ module github.com/foomo/keel
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/avast/retry-go v3.0.0+incompatible
|
||||
github.com/foomo/gotsrpc/v2 v2.6.2
|
||||
github.com/go-logr/logr v1.2.3
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
|
||||
2
go.sum
2
go.sum
@ -62,6 +62,8 @@ github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8
|
||||
github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
|
||||
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
|
||||
103
net/http/roundtripware/retry.go
Normal file
103
net/http/roundtripware/retry.go
Normal file
@ -0,0 +1,103 @@
|
||||
package roundtripware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/avast/retry-go"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type (
|
||||
RetryOptions struct {
|
||||
Handler RetryHandler
|
||||
retryOptions []retry.Option
|
||||
}
|
||||
RetryHandler func(*http.Response) error
|
||||
RetryOption func(*RetryOptions)
|
||||
)
|
||||
|
||||
func GetDefaultRetryOptions() RetryOptions {
|
||||
return RetryOptions{
|
||||
Handler: func(resp *http.Response) error {
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New("status code not ok")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func RetryWithAttempts(v uint) RetryOption {
|
||||
return func(o *RetryOptions) {
|
||||
o.retryOptions = append(o.retryOptions, retry.Attempts(v))
|
||||
}
|
||||
}
|
||||
|
||||
func RetryWithContext(ctx context.Context) RetryOption {
|
||||
return func(o *RetryOptions) {
|
||||
o.retryOptions = append(o.retryOptions, retry.Context(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
func RetryWithDelay(delay time.Duration) RetryOption {
|
||||
return func(o *RetryOptions) {
|
||||
o.retryOptions = append(o.retryOptions, retry.Delay(delay))
|
||||
}
|
||||
}
|
||||
|
||||
func RetryWithMaxDelay(maxDelay time.Duration) RetryOption {
|
||||
return func(o *RetryOptions) {
|
||||
o.retryOptions = append(o.retryOptions, retry.MaxDelay(maxDelay))
|
||||
}
|
||||
}
|
||||
|
||||
func RetryWithDelayType(delayType retry.DelayTypeFunc) RetryOption {
|
||||
return func(o *RetryOptions) {
|
||||
o.retryOptions = append(o.retryOptions, retry.DelayType(delayType))
|
||||
}
|
||||
}
|
||||
|
||||
func RetryWithOnRetry(onRetry retry.OnRetryFunc) RetryOption {
|
||||
return func(o *RetryOptions) {
|
||||
o.retryOptions = append(o.retryOptions, retry.OnRetry(onRetry))
|
||||
}
|
||||
}
|
||||
|
||||
func RetryWithLastErrorOnly(lastErrorOnly bool) RetryOption {
|
||||
return func(o *RetryOptions) {
|
||||
o.retryOptions = append(o.retryOptions, retry.LastErrorOnly(lastErrorOnly))
|
||||
}
|
||||
}
|
||||
|
||||
func RetryWithRetryIf(retryIf retry.RetryIfFunc) RetryOption {
|
||||
return func(o *RetryOptions) {
|
||||
o.retryOptions = append(o.retryOptions, retry.RetryIf(retryIf))
|
||||
}
|
||||
}
|
||||
|
||||
// Retry returns a RoundTripper which retries failed requests
|
||||
func Retry(opts ...RetryOption) RoundTripware {
|
||||
o := GetDefaultRetryOptions()
|
||||
for _, opt := range opts {
|
||||
if opt != nil {
|
||||
opt(&o)
|
||||
}
|
||||
}
|
||||
return func(l *zap.Logger, next Handler) Handler {
|
||||
return func(req *http.Request) (*http.Response, error) {
|
||||
var resp *http.Response
|
||||
err := retry.Do(func() error {
|
||||
var err error
|
||||
resp, err = next(req) //nolint:bodyclose
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.Handler(resp)
|
||||
}, o.retryOptions...)
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user