Documentation
¶
Overview ¶
Example (ConfigNext) ¶
package main
import (
"net/http"
"sync/atomic"
"time"
"github.com/cristalhq/hedgedhttp"
)
func main() {
rt := &observableRoundTripper{
rt: http.DefaultTransport,
}
cfg := hedgedhttp.Config{
Transport: rt,
Upto: 3,
Delay: 50 * time.Millisecond,
Next: func() (upto int, delay time.Duration) {
return 3, rt.MaxLatency()
},
}
client, err := hedgedhttp.New(cfg)
if err != nil {
panic(err)
}
// or client.Do
resp, err := client.RoundTrip(&http.Request{})
_ = resp
}
type observableRoundTripper struct {
rt http.RoundTripper
maxLatency atomic.Uint64
}
func (ort *observableRoundTripper) MaxLatency() time.Duration {
return time.Duration(ort.maxLatency.Load())
}
func (ort *observableRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
start := time.Now()
resp, err := ort.rt.RoundTrip(req)
if err != nil {
return resp, err
}
took := uint64(time.Since(start).Nanoseconds())
for {
max := ort.maxLatency.Load()
if max >= took {
return resp, err
}
if ort.maxLatency.CompareAndSwap(max, took) {
return resp, err
}
}
}
Output:
Example (Instrumented) ¶
package main
import (
"net/http"
"time"
"github.com/cristalhq/hedgedhttp"
)
func main() {
transport := &InstrumentedTransport{
Transport: http.DefaultTransport,
}
_, err := hedgedhttp.NewRoundTripper(time.Millisecond, 3, transport)
if err != nil {
panic(err)
}
}
type InstrumentedTransport struct {
Transport http.RoundTripper
}
func (t *InstrumentedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := t.Transport.RoundTrip(req)
if err != nil {
return nil, err
}
return resp, nil
}
Output:
Example (Ratelimited) ¶
package main
import (
"context"
"errors"
"math/rand"
"net/http"
"time"
"github.com/cristalhq/hedgedhttp"
)
func main() {
transport := &RateLimitedHedgedTransport{
Transport: http.DefaultTransport,
Limiter: &RandomRateLimiter{},
}
_, err := hedgedhttp.NewRoundTripper(time.Millisecond, 3, transport)
if err != nil {
panic(err)
}
}
// by example https://pkg.go.dev/golang.org/x/time/rate
type RateLimiter interface {
Wait(ctx context.Context) error
}
type RateLimitedHedgedTransport struct {
Transport http.RoundTripper
Limiter RateLimiter
}
func (t *RateLimitedHedgedTransport) RoundTrip(r *http.Request) (*http.Response, error) {
if hedgedhttp.IsHedgedRequest(r) {
if err := t.Limiter.Wait(r.Context()); err != nil {
return nil, err
}
}
return t.Transport.RoundTrip(r)
}
// Just for the example.
type RandomRateLimiter struct{}
func (r *RandomRateLimiter) Wait(ctx context.Context) error {
if rand.Int()%2 == 0 {
return errors.New("rate limit exceed")
}
return nil
}
Output:
Index ¶
- func IsHedgedRequest(r *http.Request) bool
- func NewClient(timeout time.Duration, upto int, client *http.Client) (*http.Client, error)
- func NewRoundTripper(timeout time.Duration, upto int, rt http.RoundTripper) (http.RoundTripper, error)
- type Client
- type Config
- type NextFn
- type Stats
- func (s *Stats) ActualRoundTrips() uint64
- func (s *Stats) CanceledByUserRoundTrips() uint64
- func (s *Stats) CanceledSubRequests() uint64
- func (s *Stats) FailedRoundTrips() uint64
- func (s *Stats) HedgedRequestWins() uint64
- func (s *Stats) OriginalRequestWins() uint64
- func (s *Stats) RequestedRoundTrips() uint64
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsHedgedRequest ¶ added in v0.6.2
IsHedgedRequest reports when a request is hedged.
func NewClient ¶
NewClient returns a new http.Client which implements hedged requests pattern. Given Client starts a new request after a timeout from previous request. Starts no more than upto requests.
func NewRoundTripper ¶
func NewRoundTripper(timeout time.Duration, upto int, rt http.RoundTripper) (http.RoundTripper, error)
NewRoundTripper returns a new http.RoundTripper which implements hedged requests pattern. Given RoundTripper starts a new request after a timeout from previous request. Starts no more than upto requests.
Types ¶
type Client ¶ added in v0.9.0
type Client struct {
// contains filtered or unexported fields
}
Client represents a hedged HTTP client.
Example ¶
package main
import (
"context"
"net/http"
"time"
"github.com/cristalhq/hedgedhttp"
)
func main() {
ctx := context.Background()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://cristalhq.dev", http.NoBody)
if err != nil {
panic(err)
}
timeout := 10 * time.Millisecond
upto := 7
client := &http.Client{Timeout: time.Second}
hedged, err := hedgedhttp.NewClient(timeout, upto, client)
if err != nil {
panic(err)
}
// will take `upto` requests, with a `timeout` delay between them
resp, err := hedged.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
// and do something with resp
}
Output:
func (*Client) Do ¶ added in v0.9.0
Do does the same as [RoundTrip], this method is presented to align with net/http.Client.
func (*Client) RoundTrip ¶ added in v0.9.0
RoundTrip implements net/http.RoundTripper interface.
type Config ¶ added in v0.9.0
type Config struct {
// Transport of the [Client].
// Default is nil which results in [net/http.DefaultTransport].
Transport http.RoundTripper
// Upto says how much requests to make.
// Default is zero which means no hedged requests will be made.
Upto int
// Delay before 2 consecutive hedged requests.
Delay time.Duration
// Next returns the upto and delay for each HTTP that will be hedged.
// Default is nil which results in (Upto, Delay) result.
Next NextFn
// contains filtered or unexported fields
}
Config for the Client.
type NextFn ¶ added in v0.9.0
NextFn represents a function that is called for each HTTP request for retrieving hedging options.
type Stats ¶ added in v0.6.0
type Stats struct {
// contains filtered or unexported fields
}
Stats object that can be queried to obtain certain metrics and get better observability.
func NewClientAndStats ¶ added in v0.6.0
func NewClientAndStats(timeout time.Duration, upto int, client *http.Client) (*http.Client, *Stats, error)
NewClientAndStats returns a new http.Client which implements hedged requests pattern And Stats object that can be queried to obtain client's metrics. Given Client starts a new request after a timeout from previous request. Starts no more than upto requests.
func NewRoundTripperAndStats ¶ added in v0.6.0
func NewRoundTripperAndStats(timeout time.Duration, upto int, rt http.RoundTripper) (http.RoundTripper, *Stats, error)
NewRoundTripperAndStats returns a new http.RoundTripper which implements hedged requests pattern And Stats object that can be queried to obtain client's metrics. Given RoundTripper starts a new request after a timeout from previous request. Starts no more than upto requests.
func (*Stats) ActualRoundTrips ¶ added in v0.6.0
ActualRoundTrips returns count of requests that were actually sent.
func (*Stats) CanceledByUserRoundTrips ¶ added in v0.6.0
CanceledByUserRoundTrips returns count of requests that were canceled by user, using request context.
func (*Stats) CanceledSubRequests ¶ added in v0.6.0
CanceledSubRequests returns count of hedged sub-requests that were canceled by transport.
func (*Stats) FailedRoundTrips ¶ added in v0.6.0
FailedRoundTrips returns count of requests that failed.
func (*Stats) HedgedRequestWins ¶ added in v0.8.0
HedgedRequestWins returns count of hedged requests that were faster than the original.
func (*Stats) OriginalRequestWins ¶ added in v0.8.0
OriginalRequestWins returns count of original requests that were faster than the original.
func (*Stats) RequestedRoundTrips ¶ added in v0.6.0
RequestedRoundTrips returns count of requests that were requested by client.