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/firestore v1.9.0 // indirect
|
||||||
cloud.google.com/go/longrunning v0.3.0 // indirect
|
cloud.google.com/go/longrunning v0.3.0 // indirect
|
||||||
github.com/armon/go-metrics v0.4.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/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // 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-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 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/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 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
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=
|
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
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/avast/retry-go v3.0.0+incompatible
|
||||||
github.com/foomo/gotsrpc/v2 v2.6.2
|
github.com/foomo/gotsrpc/v2 v2.6.2
|
||||||
github.com/go-logr/logr v1.2.3
|
github.com/go-logr/logr v1.2.3
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
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-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 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/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 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
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=
|
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