Heimdall - Golang learning step 8-1
- 公開日
- カテゴリ:APIClients
- タグ:Golang,roadmap.sh,学習メモ
roadmap.sh > Go > API Clients > REST > Heimdall の学習を進めていきます。
※ 学習メモとしての記録ですが、後にこのセクションを学ぶ道しるべとなるよう、ですます調で記載しています。
contents
開発環境
- チップ: Apple M2 Pro
- OS: macOS Sonoma
- go version: go1.23.2 darwin/arm64
参考 URL
Heimdall
Heimdall は、Go で堅牢な HTTP クライアントを構築するためのライブラリです。リトライ機能やカスタムバックオフ戦略などをサポートしており、信頼性の高い API 呼び出しを簡単に実現できます。ここでは Heimdall の基本的な使い方を解説します。
Heimdall の特徴
Heimdall は以下のような機能を提供します。
- リトライ機能:失敗した HTTP リクエストを指定回数再試行します。
- バックオフ戦略:リトライ時の待機時間を制御できます(例: 固定、指数、Jitter)。
- タイムアウトの設定:HTTP リクエストにタイムアウトを設定できます。
- カスタマイズ可能:独自のバックオフ戦略や HTTP クライアントを実装可能です。
Heimdall のインストール
Heimdall をプロジェクトに追加するには、以下のコマンドを実行します:
go get -u github.com/gojek/heimdall/v7
基本的な使い方
以下に Heimdall を使ったシンプルな例を示します。
1. GET リクエストの送信例
GET リクエストは最も基本的な HTTP リクエストで、サーバーからデータを取得するために使用されます。Heimdall を使用すると、リトライやタイムアウトを簡単に設定できます。
package main
import (
"fmt"
"io"
"log"
"time"
"github.com/gojek/heimdall/v7/httpclient"
)
func main() {
// Heimdall HTTP クライアントを作成
client := httpclient.NewClient(
httpclient.WithHTTPTimeout(10*time.Second), // 10秒のタイムアウトを設定
httpclient.WithRetryCount(3), // リトライ回数を3回に設定
)
// GET リクエストを送信
resp, err := client.Get("https://httpbin.org/get", nil)
if err != nil {
log.Fatalf("リクエスト失敗: %v", err)
}
defer resp.Body.Close()
// レスポンスの内容を出力
body, _ := io.ReadAll(resp.Body)
fmt.Printf("ステータスコード: %d\n", resp.StatusCode)
fmt.Printf("レスポンスボディ: %s\n", string(body))
}
実行結果:
ステータスコード: 200
レスポンスボディ: {
"args": {},
"headers": {
"Accept-Encoding": "gzip",
"Host": "httpbin.org",
"User-Agent": "Go-http-client/2.0",
"X-Amzn-Trace-Id": "Root=1-67849d2b-4d02c15a4caca51b640a9756"
},
"origin": "124.215.81.43",
"url": "https://httpbin.org/get"
}
POST リクエストの送信例
POST リクエストは、データをサーバーに送信するために使用されます。例えば、JSON データをサーバーに送信する際に使用します。
package main
import (
"bytes"
"fmt"
"io"
"log"
"net/http"
"time"
"github.com/gojek/heimdall/v7/httpclient"
)
func main() {
// Heimdall HTTP クライアントを作成
client := httpclient.NewClient(
httpclient.WithHTTPTimeout(10*time.Second), // 10秒のタイムアウトを設定
httpclient.WithRetryCount(3), // リトライ回数を3回に設定
)
// JSON データを送信
data := []byte(`{"message": "Hello, Heimdall!"}`)
headers := http.Header{
"Content-Type": []string{"application/json"},
}
// POST リクエストを送信
resp, err := client.Post("https://httpbin.org/post", bytes.NewBuffer(data), headers)
if err != nil {
log.Fatalf("リクエスト失敗: %v", err)
}
defer resp.Body.Close()
// レスポンスの内容を出力
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatalf("レスポンス読み取り失敗: %v", err)
}
fmt.Printf("ステータスコード: %d\n", resp.StatusCode)
fmt.Printf("レスポンスボディ: %s\n", string(body))
}
実行結果:
ステータスコード: 200
レスポンスボディ: {
"args": {},
"data": "{\"message\": \"Hello, Heimdall!\"}",
"files": {},
"form": {},
"headers": {
"Accept-Encoding": "gzip",
"Content-Length": "31",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "Go-http-client/2.0",
"X-Amzn-Trace-Id": "Root=1-67849d7f-3764380445ce957652576dd5"
},
"json": {
"message": "Hello, Heimdall!"
},
"origin": "124.215.81.43",
"url": "https://httpbin.org/post"
}
バックオフを使ったリトライ
固定バックオフを使ったリトライ
固定バックオフとは、リトライ間隔を一定時間に設定する戦略です。たとえば、失敗したリクエストを 2 秒間隔で再試行するといった設定が可能です。この戦略はリトライ間隔が一定で予測可能であるため、シンプルなケースで効果的です。
以下は、固定バックオフを利用してリトライを実装するコード例です。この例では、2 秒間隔でリトライを行い、HTTP リクエストにタイムアウトを設定しています。
package main
import (
"fmt"
"net/http"
"time"
"github.com/gojek/heimdall/v7/httpclient"
"github.com/gojek/heimdall/v7/plugins"
)
func main() {
// 固定バックオフを設定(リトライ間隔: 2秒)
backoff := heimdall.NewConstantBackoff(2*time.Second, 1*time.Second)
// HTTP クライアントを作成
client := httpclient.NewClient(
httpclient.WithHTTPTimeout(10*time.Second), // タイムアウトを10秒に設定
httpclient.WithRetrier(heimdall.NewRetrier(backoff)), // リトライ機能を設定
)
// リクエストを実行
response, err := client.Get("https://example.com", nil)
if err != nil {
fmt.Printf("リクエスト失敗: %v\n", err)
return
}
fmt.Printf("レスポンスコード: %d\n", response.StatusCode)
}
- 固定バックオフの設定
heimdall.NewConstantBackoff
を使用して固定バックオフを作成しています。2 秒の間隔と 1 秒の jitter を設定しており、リトライ間隔にランダムな要素を加えることで、全リクエストが同時に再試行される「スパイク」を回避できます。
- タイムアウトの設定
httpclient.WithHTTPTimeout
を使い、リクエストが 10 秒を超えた場合にタイムアウトするよう設定しています。これにより、無限に待機することを防ぎます。
- リトライの実行
- HTTP クライアントの Get メソッドを使用してリクエストを送信しています。失敗した場合は、指定した固定バックオフの設定に基づいて再試行されます。
指数バックオフを使ったリトライ
指数バックオフは、リトライ間隔を段階的に増やす戦略です。たとえば、最初は 500 ミリ秒後に再試行し、次は 1 秒、次は 2 秒というように、間隔を倍増させていきます。この戦略は、リソースへの負荷を抑えつつ、再試行による成功の可能性を高めたい場合に有効です。
以下は、指数バックオフを利用してリトライを実装するコード例です。この例では、初期間隔 500 ミリ秒から最大 5 秒間隔まで、リトライを繰り返す設定になっています。
package main
import (
"fmt"
"net/http"
"time"
"github.com/gojek/heimdall/v7/httpclient"
)
func main() {
// 指数バックオフを設定
backoff := heimdall.NewExponentialBackoff(
500*time.Millisecond, // 初期待機時間(リトライの最初の間隔)
5*time.Second, // 最大待機時間(リトライ間隔の上限)
float64(15*time.Second), // 最大経過時間(リトライ全体の制限時間)
2.0, // 指数増加率(リトライ間隔を倍々に増加)
)
// HTTP クライアントを作成
client := httpclient.NewClient(
httpclient.WithHTTPTimeout(10*time.Second), // タイムアウトを 10 秒に設定
httpclient.WithRetrier(heimdall.NewRetrier(backoff)), // リトライ機能を設定
)
// GET リクエストを実行
response, err := client.Get("https://example.com", nil)
if err != nil {
fmt.Printf("リクエスト失敗: %v\n", err)
return
}
// レスポンスコードを出力
fmt.Printf("レスポンスコード: %d\n", response.StatusCode)
}
- 指数バックオフの設定
heimdall.NewExponentialBackoff
を使用して、初期の待機時間を 500 ミリ秒、最大待機時間を 5 秒、最大リトライ時間を 15 秒に設定しています。リトライごとに待機時間が増加するため、短時間での連続的なリトライを防ぎ、サーバー負荷の軽減に役立ちます。
- リトライの管理
heimdall.NewRetrier
を使って、バックオフ戦略を持つリトライ機能を HTTP クライアントに設定しています。これにより、サーバーの過負荷やネットワークエラーが原因の失敗時に適切な再試行が行われます。
- タイムアウトの設定
httpclient.WithHTTPTimeout
により、10 秒を超えるリクエストは失敗として処理されます。これにより、長いリトライ待機による無限ループを防止します。
まとめ
- Heimdall は Go 言語向けの堅牢な HTTP クライアントライブラリ
- リトライ機能やバックオフ戦略を簡単に実装可能
- 固定バックオフや指数バックオフを活用した信頼性の高い API 呼び出しの実現
- HTTP クライアントのタイムアウトやカスタム設定の柔軟性
- 初心者でも理解しやすい API デザインと豊富な拡張性
[Next] Step 8-2: Grequests
[Prev] Step 7-1: Melody