Lompat ke konten
dokumentasi.id
Commit ecba1e8
v2
Star2,8 rb

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

4.1

Option pada httpclient

httpclient tidak memakai struct konfigurasi terpisah. Seluruh perilaku klien diatur lewat satu tipe fungsi, Option, yang dideklarasikan sebagai type Option func(*Client) httpclient/options.go:11-12. Setiap fungsi With... mengembalikan closure yang menerima *Client dan menulis satu field secara langsung. NewClient membuat Client dengan nilai default, menjalankan setiap option berurutan, lalu memanggil client.updateHTTPTimeout() sekali di akhir httpclient/client.go:35-49.

Arah setiap option

graph LR
  subgraph opts["Option: func(*Client)"]
    direction TB
    o1["WithHTTPTimeout"]
    o2["WithHTTPClient"]
    o3["WithRetryCount"]
    o4["WithRetrier"]
    o5["WithRetryableStatusCodes"]
    o6["WithRetryErrorBudgetToken"]
    o7["WithRetryErrorBudgetPercent"]
  end
  o1 --> f1["c.timeout *time.Duration"]
  o2 --> f2["c.client heimdall.Doer"]
  o3 --> f3["c.retryCount int"]
  o4 --> f4["c.retrier heimdall.Retriable"]
  o5 --> f5["c.retryableCodes []int"]
  o6 --> f6["c.retryErrorBudget internal.ErrorBudget"]
  o7 --> f6
  f1 --> u["updateHTTPTimeout"]
  f2 --> u
  u --> t["http.Client.Timeout"]
  f3 --> d["Client.Do retry loop"]
  f4 --> d
  f5 --> d
  f6 --> s["skipRetry"]
  s --> d
  style o1 fill:#f59e0b,color:#000
  style o2 fill:#f59e0b,color:#000
  style o3 fill:#f59e0b,color:#000
  style o4 fill:#f59e0b,color:#000
  style o5 fill:#f59e0b,color:#000
  style o6 fill:#f59e0b,color:#000
  style o7 fill:#f59e0b,color:#000
  style f1 fill:#336791,color:#fff
  style f2 fill:#336791,color:#fff
  style f3 fill:#336791,color:#fff
  style f4 fill:#336791,color:#fff
  style f5 fill:#336791,color:#fff
  style f6 fill:#8b5cf6,color:#fff
  style u fill:#8b5cf6,color:#fff
  style t fill:#2d7d46,color:#fff
  style d fill:#2d7d46,color:#fff
  style s fill:#dc2626,color:#fff
%% fig: Sambungan setiap Option ke field Client
%% desc: Tujuh fungsi With menulis enam field Client; timeout dan client bermuara ke updateHTTPTimeout, sisanya dipakai di loop Do dan skipRetry.
Fig. 1: Sambungan setiap Option ke field ClientTujuh fungsi With menulis enam field Client; timeout dan client bermuara ke updateHTTPTimeout, sisanya dipakai di loop Do dan skipRetry.

Tujuh fungsi With... hanya menulis field yang sudah ada pada struct Client: client, timeout, retrier, retryCount, retryableCodes, dan retryErrorBudget httpclient/client.go:16-25. Dua option berhenti di tahap konfigurasi, yaitu WithHTTPTimeout dan WithHTTPClient, karena keduanya memicu updateHTTPTimeout yang menyalin c.timeout ke http.Client.Timeout httpclient/client.go:222-229. Empat option lain baru terpakai saat request berjalan: retryCount menentukan panjang loop Do, retrier memasok jeda, sedangkan retryableCodes dan retryErrorBudget menentukan apakah percobaan diulang atau dihentikan.

Daftar option

OptionField tujuanPerilaku
WithHTTPTimeoutc.timeout *time.DurationMenyimpan durasi lalu memanggil c.updateHTTPTimeout() httpclient/options.go:15-21
WithRetryCountc.retryCount intMenetapkan jumlah percobaan ulang httpclient/options.go:24-28
WithRetrierc.retrier heimdall.RetriableMenetapkan strategi jeda antar percobaan httpclient/options.go:31-35
WithHTTPClientc.client heimdall.DoerMengganti implementasi HTTP client, lalu memanggil c.updateHTTPTimeout() httpclient/options.go:38-44
WithRetryableStatusCodesc.retryableCodes []intMenambahkan status code ke daftar lalu mengurutkannya dengan slices.Sort httpclient/options.go:46-55
WithRetryErrorBudgetTokenc.retryErrorBudget *internal.ErrorBudgetMembuat budget lewat internal.NewTokenErrorBudget(maxToken, tokenRatio) httpclient/options.go:61-65
WithRetryErrorBudgetPercentc.retryErrorBudget *internal.ErrorBudgetMembuat budget lewat internal.NewPercentErrorBudget(minFailureVolume, failurePercent) httpclient/options.go:82-86

WithRetryCount juga berpengaruh pada penyiapan request: Do hanya memanggil internal.SetRequestGetBody ketika c.retryCount > 0, sehingga request tanpa retry tidak menanggung overhead tambahan httpclient/client.go:133-139. Rincian loop dan jedanya ada di Siklus Request dan Retry serta Retry dan Backoff.

Status code yang diulang

WithRetryableStatusCodes menerima variadic statusCodes ...int, menggabungkannya dengan daftar yang sudah ada, lalu mengurutkan hasilnya httpclient/options.go:46-55. Pengurutan itu bukan hiasan: Do memeriksa daftar tersebut dengan slices.BinarySearch httpclient/client.go:179-181. Selain daftar itu, seluruh status 5xx selalu layak diulang, seperti dicatat pada komentar option-nya: "All 5xx status codes are always eligible for retry, thus not required for WithRetryableStatusCodes option." httpclient/options.go:46-47

Test TestOptionsAreSet memasang WithRetryableStatusCodes(400, 201, 424) dan memastikan field akhirnya berisi []int{201, 400, 424}, bukti bahwa urutan masukan tidak dipertahankan httpclient/options_test.go:13-38.

Timeout: WithHTTPTimeout berhadapan dengan WithHTTPClient

updateHTTPTimeout langsung kembali bila c.timeout == nil, dan hanya menulis bila c.client benar-benar bertipe *http.Client httpclient/client.go:222-229. Dari situ muncul dua perilaku yang perlu diingat.

Skenario optionc.timeoutTimeout pada HTTP client
Hanya WithHTTPClient(client)nilTimeout pemanggil dipertahankan, 25 * time.Millisecond httpclient/options_test.go:40-52
WithHTTPClient lalu WithHTTPTimeout(10 * time.Second)10 * time.Second10 * time.Second httpclient/options_test.go:54-69
WithHTTPTimeout lalu WithHTTPClient10 * time.Second10 * time.Second httpclient/options_test.go:71-86

Urutan dua option itu tidak mengubah hasil untuk *http.Client, karena NewClient menutup rangkaian option dengan satu panggilan updateHTTPTimeout httpclient/client.go:35-49. Yang tidak bisa ditembus adalah implementasi Doer buatan sendiri: bila c.client bukan *http.Client, timeout tidak pernah ditulis ke dalamnya, sesuai komentar pengujian "can't override custom implementation" httpclient/options_test.go:13-38.

⚠️ WithHTTPTimeout tidak mengubah timeout di dalam Doer kustom. Nilai timeout milik implementasi buatan sendiri tetap berlaku, sehingga c.timeout bisa berbeda dari timeout yang benar-benar dipakai. Pasang timeout di dalam tipe kustom itu sendiri bila memakai WithHTTPClient.

Pola yang sama dipakai klien circuit breaker pada Option dan Fallback hystrix, karena hystrix.WithHTTPTimeout bersandar pada mekanisme ini, seperti ditulis pada komentar di dalam WithHTTPTimeout httpclient/options.go:15-21.

Nilai default tanpa option

NewClient mengisi client dengan &http.Client{Timeout: defaultHTTPTimeout}, retryCount dengan defaultRetryCount, dan retrier dengan heimdall.NewNoRetrier(); dua konstanta default itu bernilai 0 dan 30 * time.Second httpclient/client.go:27-30. TestOptionsHaveDefaults memastikan c.timeout tetap nil dan c.retrier adalah NewNoRetrier() ketika NewClient() dipanggil tanpa argumen httpclient/options_test.go:88-105.

Karena timeout dibiarkan nil, updateHTTPTimeout tidak menyentuh http.Client bawaan, dan timeout 30 detik dari literal di NewClient tetap utuh httpclient/client.go:35-49.

Batas budget token

WithRetryErrorBudgetToken(maxToken int32, tokenRatio float32) meneruskan nilainya ke internal.NewTokenErrorBudget httpclient/options.go:61-65, dan konstruktor itu mem-panic bila maxToken melampaui maxAllowedToken, yaitu ((1 << 30) - 1) / normaliser dengan normaliser = 10000 internal/error_budget.go:8-9. Ambang itu bernilai 107374, jadi WithRetryErrorBudgetToken(200000, 1) akan gagal saat option dijalankan, bukan saat request pertama.

⚠️ Pelanggaran batas token memicu panic dengan pesan "max token exceeds allowed limit (%d)" dari konstruktor internal.NewTokenErrorBudget internal/error_budget.go:25-28. Validasi maxToken sebelum membangun klien, jangan mengandalkan error yang dikembalikan Do.

WithRetryErrorBudgetPercent tidak meminta maxToken; ia menghitungnya dari minFailureVolume * 2 di dalam internal.NewPercentErrorBudget httpclient/options.go:82-86. Kedua option menulis field yang sama, c.retryErrorBudget, sehingga option yang dijalankan terakhir menang. Pemakaian field itu di skipRetry dijelaskan di Error Budget untuk Retry.

Contoh pemakaian

Pengujian TestOptionsAreSet merangkai lima option sekaligus dan memeriksa setiap field hasilnya httpclient/options_test.go:13-38. Contoh yang lebih lengkap, dengan WithHTTPClient yang membungkus http.Client milik pemanggil, ada di berkas contoh repository:

httpClient := httpclient.NewClient(
	httpclient.WithHTTPTimeout(0*time.Millisecond),
	httpclient.WithHTTPClient(&myHTTPClient{
		client: http.Client{Timeout: 25 * time.Millisecond},
	}),
	httpclient.WithRetryCount(2),
	httpclient.WithRetrier(heimdall.NewRetrier(heimdall.NewConstantBackoff(10*time.Millisecond, 50*time.Millisecond))),
)

Perhatikan bahwa WithHTTPTimeout dipanggil lebih dulu daripada WithHTTPClient di contoh tersebut; hasil akhirnya tetap konsisten karena penutup rangkaian di NewClient examples/client.go:82-91.