Heimdall
An enhanced HTTP client for Go
Install / Use
/learn @gojek/HeimdallREADME
Heimdall
<p align="center"><img src="doc/heimdall-logo.png" width="360"></p> <p align="center"> <a href="https://github.com/gojek/heimdall/actions"><img src="https://github.com/gojek/heimdall/actions/workflows/ci.yml/badge.svg" alt="Build Status"></img></a> <a href="https://goreportcard.com/report/github.com/gojek/heimdall"><img src="https://goreportcard.com/badge/github.com/gojek/heimdall"></img></a> <a href="https://golangci.com"><img src="https://golangci.com/badges/github.com/gojek/heimdall.svg"></img></a> <a href="https://coveralls.io/github/gojek/heimdall?branch=master"><img src="https://coveralls.io/repos/github/gojek/heimdall/badge.svg?branch=master"></img></a> </p>Description
Heimdall is an HTTP client that helps your application make a large number of requests, at scale. With Heimdall, you can:
- Use a hystrix-like circuit breaker to control failing requests
- Add synchronous in-memory retries to each request, with the option of setting your own retrier strategy
- Create clients with different timeouts for every request
All HTTP methods are exposed as a fluent interface.
Installation
go get -u github.com/gojek/heimdall/v7
Usage
Importing the package
This package can be used by adding the following import statement to your .go files.
import "github.com/gojek/heimdall/v7/httpclient"
Making a simple GET request
The below example will print the contents of the google home page:
// Create a new HTTP client with a default timeout
timeout := 1000 * time.Millisecond
client := httpclient.NewClient(httpclient.WithHTTPTimeout(timeout))
// Use the clients GET method to create and execute the request
res, err := client.Get("http://google.com", nil)
if err != nil{
panic(err)
}
// Heimdall returns the standard *http.Response object
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body))
You can also use the *http.Request object with the http.Do interface :
timeout := 1000 * time.Millisecond
client := httpclient.NewClient(httpclient.WithHTTPTimeout(timeout))
// Create an http.Request instance
req, _ := http.NewRequest(http.MethodGet, "http://google.com", nil)
// Call the `Do` method, which has a similar interface to the `http.Do` method
res, err := client.Do(req)
if err != nil {
panic(err)
}
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body))
Creating a hystrix-like circuit breaker
To import hystrix package of heimdall.
import "github.com/gojek/heimdall/v7/hystrix"
You can use the hystrix.NewClient function to create a client wrapped in a hystrix-like circuit breaker:
// Create a new hystrix-wrapped HTTP client with the command name, along with other required options
client := hystrix.NewClient(
hystrix.WithHTTPTimeout(10 * time.Millisecond),
hystrix.WithCommandName("google_get_request"),
hystrix.WithHystrixTimeout(1000 * time.Millisecond),
hystrix.WithMaxConcurrentRequests(30),
hystrix.WithErrorPercentThreshold(20),
hystrix.WithStatsDCollector("localhost:8125", "myapp.hystrix"),
)
// The rest is the same as the previous example
In the above example, there are two timeout values used: one for the hystrix configuration, and one for the HTTP client configuration. The former determines the time at which hystrix should register an error, while the latter determines when the client itself should return a timeout error. Unless you have any special requirements, both of these would have the same values.
You can choose to export hystrix metrics to a statsD collector with the hystrix.WithStatsDCollector(<statsd addr>, <metrics-prefix>) option when initializing the client as shown above.
Creating a hystrix-like circuit breaker with fallbacks
You can use the hystrix.NewClient function to create a client wrapped in a hystrix-like circuit breaker by passing in your own custom fallbacks:
The fallback function will trigger when your code returns an error, or whenever it is unable to complete based on a variety of health checks.
How your fallback function should look like you should pass in a function whose signature looks like following
func(err error) error {
// your logic for handling the error/outage condition
return err
}
Example
// Create a new fallback function
fallbackFn := func(err error) error {
_, err := http.Post("post_to_channel_two")
return err
}
timeout := 10 * time.Millisecond
// Create a new hystrix-wrapped HTTP client with the fallbackFunc as fall-back function
client := hystrix.NewClient(
hystrix.WithHTTPTimeout(timeout),
hystrix.WithCommandName("MyCommand"),
hystrix.WithHystrixTimeout(1100 * time.Millisecond),
hystrix.WithMaxConcurrentRequests(100),
hystrix.WithErrorPercentThreshold(20),
hystrix.WithSleepWindow(10),
hystrix.WithRequestVolumeThreshold(10),
hystrix.WithFallbackFunc(fallbackFn),
})
// The rest is the same as the previous example
In the above example, the fallbackFunc is a function which posts to channel two in case posting to channel one fails.
Creating an HTTP client with a retry mechanism
// First set a backoff mechanism. Constant backoff increases the backoff at a constant rate
backoffInterval := 2 * time.Millisecond
// Define a maximum jitter interval. It must be more than 1*time.Millisecond
maximumJitterInterval := 5 * time.Millisecond
backoff := heimdall.NewConstantBackoff(backoffInterval, maximumJitterInterval)
// Create a new retry mechanism with the backoff
retrier := heimdall.NewRetrier(backoff)
timeout := 1000 * time.Millisecond
// Create a new client, sets the retry mechanism, and the number of times you would like to retry
client := httpclient.NewClient(
httpclient.WithHTTPTimeout(timeout),
httpclient.WithRetrier(retrier),
httpclient.WithRetryCount(4),
)
// The rest is the same as the first example
Or create client with exponential backoff
// First set a backoff mechanism. Exponential Backoff increases the backoff at a exponential rate
initalTimeout := 2*time.Millisecond // Inital timeout
maxTimeout := 9*time.Millisecond // Max time out
exponentFactor := 2 // Multiplier
maximumJitterInterval := 2*time.Millisecond // Max jitter interval. It must be more than 1*time.Millisecond
backoff := heimdall.NewExponentialBackoff(initalTimeout, maxTimeout, exponentFactor, maximumJitterInterval)
// Create a new retry mechanism with the backoff
retrier := heimdall.NewRetrier(backoff)
timeout := 1000 * time.Millisecond
// Create a new client, sets the retry mechanism, and the number of times you would like to retry
client := httpclient.NewClient(
httpclient.WithHTTPTimeout(timeout),
httpclient.WithRetrier(retrier),
httpclient.WithRetryCount(4),
)
// The rest is the same as the first example
This will create an HTTP client which will retry every 500 milliseconds incase the request fails. The library also comes with an Exponential Backoff
Custom retry mechanisms
Heimdall supports custom retry strategies. To do this, you will have to implement the Backoff interface:
type Backoff interface {
Next(retry int) time.Duration
}
Let's see an example of creating a client with a linearly increasing backoff time:
First, create the backoff mechanism:
type linearBackoff struct {
backoffInterval int
}
func (lb *linearBackoff) Next(retry int) time.Duration{
if retry <= 0 {
return 0 * time.Millisecond
}
return time.Duration(retry * lb.backoffInterval) * time.Millisecond
}
This will create a backoff mechanism, where the retry time will increase linearly for each retry attempt. We can use this to create the client, just like the last example:
backoff := &linearBackoff{100}
retrier := heimdall.NewRetrier(backoff)
timeout := 1000 * time.Millisecond
// Create a new client, sets the retry mechanism, and the number of times you would like to retry
client := httpclient.NewClient(
httpclient.WithHTTPTimeout(timeout),
httpclient.WithRetrier(retrier),
httpclient.WithRetryCount(4),
)
// The rest is the same as the first example
Heimdall also allows you to simply pass a function that returns the retry timeout. This can be used to create the client, like:
linearRetrier := NewRetrierFunc(func(retry int) time.Duration {
if retry <= 0 {
return 0 * time.Millisecond
}
return time.Duration(retry) * time.Millisecond
})
timeout := 1000 * time.Millisecond
client := httpclient.NewClient(
httpclient.WithHTTPTimeout(timeout),
httpclient.WithRetrier(linearRetrier),
httpclient.WithRetryCount(4),
)
Custom HTTP clients
Heimdall supports custom HTTP clients. This is useful if you are using a client imported from another library and/or wish to implement custom logging, cookies, headers etc for each request that you make with your client.
Under the hood, the httpClient struct now accepts Doer, which is the standard interface implemented by HTTP clients (including the standard library's net/*http.Client)
Let's say we wish to add authorization headers to all our requests.
We can define our client myHTTPClient
type myHTTPClient struc
Related Skills
node-connect
333.7kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
xurl
333.7kA CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.
frontend-design
82.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
333.7kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
