Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Httpリクエストを自動リトライ・ポーリングするイテレータを作ってみた

miyamo2
September 04, 2024

 Httpリクエストを自動リトライ・ポーリングするイテレータを作ってみた

miyamo2

September 04, 2024
Tweet

More Decks by miyamo2

Other Decks in Programming

Transcript

  1. サンプルコード url := "http://example.com" ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer

    cancel() opts := []r2.Option{ r2.WithMaxRequestAttempts(3), r2.WithPeriod(time.Second), } for resp, err := range r2.Get(ctx, url, opts...) { if err != nil { slog.WarnContext(ctx, "something happened.", slog.Any("error", err)) // Note: continueを使用してもイテレータが終了する場合がある continue } if resp == nil { slog.WarnContext(ctx, "response is nil") continue } if resp.StatusCode != http.StatusOK { slog.WarnContext(ctx, "unexpected status code.", slog.Int("expect", http.StatusOK), slog.Int("got", resp.StatusCode)) continue } buf, err := io.ReadAll(resp.Body) if err != nil { slog.ErrorContext(ctx, "failed to read response body.", slog.Any("error", err)) continue } slog.InfoContext(ctx, "response", slog.String("response", string(buf))) // r2ではデフォルトでリクエストボディが自動クローズするため明示的にクローズ処理を行う必要がない }
  2. r2がイテレータを終了する条件 リクエストが成功(ステータスコードが200 ~ 399)、 かつ WithTerminateIf が指定されていない場合 WithTerminateIf で指定された条件が満たされた場合 429:

    Too Many Requests以外の4xx クライアントエラーが返された 場合 WithMaxRequestAttempts によって指定されたリクエストの最大回数 を超えた場合 引数で渡された context.Context がキャンセルされた場合 for rangeループがbreakによって中断された場合
  3. Get 以外にr2で用意されている関数 func Head(ctx context.Context, url string, options ...Option) (*http.Response,

    error) func Post(ctx context.Context, url string, body io.Reader, options ...Option) (*http.Response, error) func Put(ctx context.Context, url string, body io.Reader, options ...Option) (*http.Response, error) func Patch(ctx context.Context, url string, body io.Reader, options ...Option) (*http.Response, error) func Delete(ctx context.Context, url string, body io.Reader, options ...Option) (*http.Response, error) func PostForm(ctx context.Context, url string, data url.Values, options ...Option) (*http.Response, error)
  4. WithPeriod func WithPeriod(period time.Duration) Option r2.WithPeriod(time.Second) 各リクエストのタイムアウト時間を指定する デフォルト、もしくは0が設定された場合はタイムアウトしない http.Client.Timeout を使用せず

    r2 独自で context.WithTimeout を用い たハンドリングを行う http.Client.Timeout と併用して同じ値が設定された場合にどちらのタ イムアウトが適用されるかの動作は未定義
  5. WithTerminateIf func WithTerminateIf(terminationCondition func(resp *http.Response, err error) bool) Option r2.WithTerminateIf(

    func(resp *http.Response, _ error) bool { buf, err := io.ReadAll(resp.Body) if err != nil { return true } data := map[string]any{} if err := json.Unmarshal(buf, data); err != nil { return false } return data["foo"] == "bar" }) ユーザー独自の終了条件を指定する レスポンスボディの巻き戻しは r2 側で対応