API reference
Package relay — the exported surface. Signatures
are stable across the v1 line; additions are documented in the
changelog.
Constructor
func New
func New(opts ...Option) *Client
Creates a client with the given options applied in order. With no options it
behaves like a plain http.Client backed by a clone of
http.DefaultTransport: a single attempt, no per-attempt timeout,
and no breaker. The returned client is safe for concurrent use.
Client
type Client
type Client struct {
// unexported fields
}
Client is a resilient HTTP client. Construct it with New
and reuse it; do not copy a Client after first use.
func (*Client) Do
func (c *Client) Do(ctx context.Context, req *http.Request) (*http.Response, error)
Sends an HTTP request and returns the response, applying the breaker, retry,
and timeout layers. The provided context governs the entire operation; if it
is cancelled, Do returns its error. The request's own context is replaced by
a derived one, so build requests with http.NewRequest rather
than http.NewRequestWithContext.
func (*Client) Get
func (c *Client) Get(ctx context.Context, url string) (*http.Response, error)
Convenience wrapper that builds a GET request and calls
Do.
func (*Client) Post
func (c *Client) Post(ctx context.Context, url, contentType string, body io.Reader) (*http.Response, error)
Convenience wrapper that builds a POST request with the given content type
and body. When body is a *bytes.Reader,
*bytes.Buffer, or *strings.Reader, the request is
retryable; otherwise relay sends it exactly once.
func (*Client) State
func (c *Client) State() BreakerState
Returns the current breaker state. Useful for health endpoints and metrics.
Returns StateClosed when the breaker is disabled.
Options
type Option
type Option func(*config)
An Option configures a Client. Use the With* helpers below
rather than constructing options directly.
| Option | Signature |
|---|---|
WithTimeout | func(d time.Duration) Option |
WithRetry | func(p RetryPolicy) Option |
WithBackoff | func(b Backoff) Option |
WithCircuitBreaker | func(c BreakerConfig) Option |
WithTransport | func(rt http.RoundTripper) Option |
Retries
type RetryPolicy
type RetryPolicy struct {
MaxAttempts int
RetryOn func(resp *http.Response, err error) bool
}
See Configuration for field semantics.
Built-in classifiers
func RetryIdempotent(resp *http.Response, err error) bool
func RetryNever(resp *http.Response, err error) bool
Backoff
type Backoff
type Backoff interface {
// Delay returns the wait before the given attempt number, where
// attempt is the 1-indexed retry (1 = before the second try).
Delay(attempt int) time.Duration
}
type ExponentialBackoff
type ExponentialBackoff struct {
Base time.Duration
Max time.Duration
Jitter JitterFunc // optional; defaults to FullJitter
}
Implements Backoff with min(Max, Base × 2^(attempt-1)),
reduced by Jitter. The package also exports
ConstantBackoff for a fixed delay.
Jitter
type JitterFunc func(d time.Duration) time.Duration
var (
FullJitter JitterFunc // uniform in [0, d]
EqualJitter JitterFunc // d/2 + uniform in [0, d/2]
NoJitter JitterFunc // returns d unchanged
)
Circuit breaker
type BreakerConfig
type BreakerConfig struct {
FailureThreshold int
SuccessThreshold int
OpenTimeout time.Duration
}
type BreakerState
type BreakerState int
const (
StateClosed BreakerState = iota
StateOpen
StateHalfOpen
)
func (s BreakerState) String() string
Errors
var (
// ErrCircuitOpen is returned when the breaker is open and a call is
// rejected without contacting the upstream.
ErrCircuitOpen = errors.New("relay: circuit breaker open")
// ErrExhausted is returned when all retry attempts have been used and
// the last attempt still failed. It wraps the underlying error.
ErrExhausted = errors.New("relay: retry attempts exhausted")
)
Both errors support errors.Is. ErrExhausted wraps
the final cause, so errors.Unwrap yields the last transport or
response error encountered.
resp, err := client.Get(ctx, url)
switch {
case errors.Is(err, relay.ErrCircuitOpen):
// upstream is being shed; serve a fallback
case errors.Is(err, relay.ErrExhausted):
// retried and still failed
case err != nil:
// context cancelled or non-retryable error
}
For end-to-end usage, see Examples.