Merge pull request #132 from foomo/feature/add-retry-rountripware

feat: retry roundtripware
This commit is contained in:
Kevin Franklin Kim 2023-02-20 10:50:49 +01:00 committed by GitHub
commit 0a22dc4913
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 183 additions and 0 deletions

View File

@ -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

View File

@ -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=

View 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
}

View File

@ -0,0 +1,13 @@
package main
import (
"time"
)
func main() {
go server()
go client()
time.Sleep(time.Second * 60)
}

View 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
View File

@ -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
View File

@ -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=

View 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
}
}
}